aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am13
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/Makefile.test_fuzz.include2
-rw-r--r--src/Makefile.test_util.include2
-rw-r--r--src/bench/block_assemble.cpp14
-rw-r--r--src/bench/duplicate_inputs.cpp8
-rw-r--r--src/bench/mempool_eviction.cpp8
-rw-r--r--src/bench/mempool_stress.cpp2
-rw-r--r--src/bench/rpc_blockchain.cpp50
-rw-r--r--src/bench/wallet_balance.cpp16
-rw-r--r--src/bitcoin-cli.cpp145
-rw-r--r--src/compat.h19
-rw-r--r--src/core_io.h2
-rw-r--r--src/core_write.cpp17
-rw-r--r--src/i2p.cpp407
-rw-r--r--src/i2p.h260
-rw-r--r--src/init.cpp29
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.h5
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/miner.cpp12
-rw-r--r--src/miner.h4
-rw-r--r--src/net.cpp163
-rw-r--r--src/net.h74
-rw-r--r--src/net_processing.cpp703
-rw-r--r--src/net_processing.h3
-rw-r--r--src/netaddress.cpp64
-rw-r--r--src/netaddress.h30
-rw-r--r--src/netbase.cpp48
-rw-r--r--src/netbase.h26
-rw-r--r--src/node/coin.cpp3
-rw-r--r--src/node/coinstats.cpp13
-rw-r--r--src/node/coinstats.h3
-rw-r--r--src/node/interfaces.cpp65
-rw-r--r--src/node/transaction.cpp7
-rw-r--r--src/qt/addresstablemodel.cpp44
-rw-r--r--src/qt/bantablemodel.cpp15
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoingui.cpp3
-rw-r--r--src/qt/clientmodel.cpp8
-rw-r--r--src/qt/forms/debugwindow.ui90
-rw-r--r--src/qt/forms/overviewpage.ui10
-rw-r--r--src/qt/guiutil.cpp8
-rw-r--r--src/qt/guiutil.h6
-rw-r--r--src/qt/peertablemodel.cpp45
-rw-r--r--src/qt/receivecoinsdialog.cpp20
-rw-r--r--src/qt/receivecoinsdialog.h3
-rw-r--r--src/qt/rpcconsole.cpp57
-rw-r--r--src/qt/rpcconsole.h10
-rw-r--r--src/qt/test/test_main.cpp7
-rw-r--r--src/qt/transactiontablemodel.cpp30
-rw-r--r--src/qt/transactionview.cpp51
-rw-r--r--src/qt/transactionview.h1
-rw-r--r--src/rpc/blockchain.cpp22
-rw-r--r--src/rpc/mining.cpp10
-rw-r--r--src/rpc/misc.cpp4
-rw-r--r--src/rpc/net.cpp22
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/test/blockfilter_index_tests.cpp2
-rw-r--r--src/test/denialofservice_tests.cpp69
-rw-r--r--src/test/fuzz/FuzzedDataProvider.h1
-rw-r--r--src/test/fuzz/addrman.cpp2
-rw-r--r--src/test/fuzz/autofile.cpp2
-rw-r--r--src/test/fuzz/banman.cpp5
-rw-r--r--src/test/fuzz/bloom_filter.cpp2
-rw-r--r--src/test/fuzz/buffered_file.cpp2
-rw-r--r--src/test/fuzz/coins_view.cpp7
-rw-r--r--src/test/fuzz/connman.cpp11
-rw-r--r--src/test/fuzz/crypto.cpp2
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp2
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp2
-rw-r--r--src/test/fuzz/data_stream.cpp5
-rw-r--r--src/test/fuzz/deserialize.cpp2
-rw-r--r--src/test/fuzz/fuzz.cpp6
-rw-r--r--src/test/fuzz/fuzz.h2
-rw-r--r--src/test/fuzz/integer.cpp6
-rw-r--r--src/test/fuzz/kitchen_sink.cpp2
-rw-r--r--src/test/fuzz/load_external_block_file.cpp4
-rw-r--r--src/test/fuzz/merkleblock.cpp2
-rw-r--r--src/test/fuzz/muhash.cpp2
-rw-r--r--src/test/fuzz/net.cpp4
-rw-r--r--src/test/fuzz/netaddress.cpp2
-rw-r--r--src/test/fuzz/node_eviction.cpp22
-rw-r--r--src/test/fuzz/policy_estimator.cpp5
-rw-r--r--src/test/fuzz/policy_estimator_io.cpp5
-rw-r--r--src/test/fuzz/pow.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp5
-rw-r--r--src/test/fuzz/process_messages.cpp5
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp2
-rw-r--r--src/test/fuzz/script.cpp2
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp2
-rw-r--r--src/test/fuzz/script_ops.cpp2
-rw-r--r--src/test/fuzz/scriptnum_ops.cpp2
-rw-r--r--src/test/fuzz/signet.cpp5
-rw-r--r--src/test/fuzz/socks5.cpp44
-rw-r--r--src/test/fuzz/string.cpp2
-rw-r--r--src/test/fuzz/strprintf.cpp2
-rw-r--r--src/test/fuzz/system.cpp2
-rw-r--r--src/test/fuzz/torcontrol.cpp80
-rw-r--r--src/test/fuzz/transaction.cpp15
-rw-r--r--src/test/fuzz/util.h151
-rw-r--r--src/test/miner_tests.cpp38
-rw-r--r--src/test/net_tests.cpp58
-rw-r--r--src/test/rpc_tests.cpp10
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/test/util/mining.cpp2
-rw-r--r--src/test/util/setup_common.cpp6
-rw-r--r--src/test/util/setup_common.h18
-rw-r--r--src/test/util_tests.cpp10
-rw-r--r--src/test/validation_block_tests.cpp4
-rw-r--r--src/test/versionbits_tests.cpp86
-rw-r--r--src/torcontrol.cpp167
-rw-r--r--src/torcontrol.h136
-rw-r--r--src/txmempool.cpp34
-rw-r--r--src/txmempool.h57
-rw-r--r--src/txorphanage.cpp202
-rw-r--r--src/txorphanage.h85
-rw-r--r--src/util/epochguard.h91
-rw-r--r--src/util/moneystr.cpp12
-rw-r--r--src/util/moneystr.h2
-rw-r--r--src/util/readwritefile.cpp47
-rw-r--r--src/util/readwritefile.h28
-rw-r--r--src/util/sock.cpp184
-rw-r--r--src/util/sock.h50
-rw-r--r--src/util/time.h13
-rw-r--r--src/validation.cpp147
-rw-r--r--src/validation.h40
-rw-r--r--src/versionbits.h3
-rw-r--r--src/wallet/feebumper.cpp2
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/rpcdump.cpp122
-rw-r--r--src/wallet/rpcwallet.cpp315
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/wallet.cpp27
-rw-r--r--src/wallet/wallet.h4
136 files changed, 3449 insertions, 1757 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6a7a49f012..e2b930a2fa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -148,6 +148,7 @@ BITCOIN_CORE_H = \
fs.h \
httprpc.h \
httpserver.h \
+ i2p.h \
index/base.h \
index/blockfilterindex.h \
index/disktxpos.h \
@@ -224,13 +225,15 @@ BITCOIN_CORE_H = \
timedata.h \
torcontrol.h \
txdb.h \
- txrequest.h \
txmempool.h \
+ txorphanage.h \
+ txrequest.h \
undo.h \
util/asmap.h \
util/bip32.h \
util/bytevectorhash.h \
util/check.h \
+ util/epochguard.h \
util/error.h \
util/fees.h \
util/getuniquepath.h \
@@ -241,6 +244,7 @@ BITCOIN_CORE_H = \
util/message.h \
util/moneystr.h \
util/rbf.h \
+ util/readwritefile.h \
util/ref.h \
util/settings.h \
util/sock.h \
@@ -300,7 +304,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# Contains code accessing mempool and chain state that is meant to be separated
# from wallet and gui code (see node/README.md). Shared code should go in
# libbitcoin_common or libbitcoin_util libraries, instead.
-libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
@@ -314,6 +318,7 @@ libbitcoin_server_a_SOURCES = \
flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
+ i2p.cpp \
index/base.cpp \
index/blockfilterindex.cpp \
index/txindex.cpp \
@@ -347,8 +352,9 @@ libbitcoin_server_a_SOURCES = \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
- txrequest.cpp \
txmempool.cpp \
+ txorphanage.cpp \
+ txrequest.cpp \
validation.cpp \
validationinterface.cpp \
versionbits.cpp \
@@ -572,6 +578,7 @@ libbitcoin_util_a_SOURCES = \
util/message.cpp \
util/moneystr.cpp \
util/rbf.cpp \
+ util/readwritefile.cpp \
util/settings.cpp \
util/threadnames.cpp \
util/spanparsing.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index e817bb2ee2..133277baa6 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -285,12 +285,14 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp \
test/fuzz/signature_checker.cpp \
test/fuzz/signet.cpp \
+ test/fuzz/socks5.cpp \
test/fuzz/span.cpp \
test/fuzz/spanparsing.cpp \
test/fuzz/string.cpp \
test/fuzz/strprintf.cpp \
test/fuzz/system.cpp \
test/fuzz/timedata.cpp \
+ test/fuzz/torcontrol.cpp \
test/fuzz/transaction.cpp \
test/fuzz/tx_in.cpp \
test/fuzz/tx_out.cpp \
diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include
index 75fe68fcd1..2d772f2fca 100644
--- a/src/Makefile.test_fuzz.include
+++ b/src/Makefile.test_fuzz.include
@@ -12,7 +12,7 @@ TEST_FUZZ_H = \
test/fuzz/FuzzedDataProvider.h \
test/fuzz/util.h
-libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_fuzz_a_SOURCES = \
test/fuzz/fuzz.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 1abfb667a0..f7f393ccac 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -19,7 +19,7 @@ TEST_UTIL_H = \
test/util/validation.h \
test/util/wallet.h
-libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_util_a_SOURCES = \
test/util/blockfilter.cpp \
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 8f656c44d9..67ab02a5b3 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -16,13 +16,7 @@
static void AssembleBlock(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
const std::vector<unsigned char> op_true{OP_TRUE};
CScriptWitness witness;
@@ -38,7 +32,7 @@ static void AssembleBlock(benchmark::Bench& bench)
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
for (size_t b{0}; b < NUM_BLOCKS; ++b) {
CMutableTransaction tx;
- tx.vin.push_back(MineBlock(test_setup.m_node, SCRIPT_PUB));
+ tx.vin.push_back(MineBlock(test_setup->m_node, SCRIPT_PUB));
tx.vin.back().scriptWitness = witness;
tx.vout.emplace_back(1337, SCRIPT_PUB);
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
@@ -48,13 +42,13 @@ static void AssembleBlock(benchmark::Bench& bench)
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
for (const auto& txr : txs) {
- const MempoolAcceptResult res = ::AcceptToMemoryPool(::ChainstateActive(), *test_setup.m_node.mempool, txr, false /* bypass_limits */);
+ const MempoolAcceptResult res = ::AcceptToMemoryPool(::ChainstateActive(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
bench.run([&] {
- PrepareBlock(test_setup.m_node, SCRIPT_PUB);
+ PrepareBlock(test_setup->m_node, SCRIPT_PUB);
});
}
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 5745e4276c..25d1a2b56c 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -14,13 +14,7 @@
static void DuplicateInputs(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
const CScript SCRIPT_PUB{CScript(OP_TRUE)};
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index db9a5661fd..4f49fba7b7 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -25,13 +25,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
// unique transactions for a more meaningful performance measurement.
static void MempoolEviction(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
CMutableTransaction tx1 = CMutableTransaction();
tx1.vin.resize(1);
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index 9b862b735c..f28768efc8 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -79,7 +79,7 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
- TestingSetup test_setup;
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index 45ed9f60dc..c8886a4c23 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -12,25 +12,49 @@
#include <univalue.h>
-static void BlockToJsonVerbose(benchmark::Bench& bench)
-{
- TestingSetup test_setup{};
+namespace {
+
+struct TestBlockAndIndex {
+ const std::unique_ptr<const TestingSetup> testing_setup{MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN)};
+ CBlock block{};
+ uint256 blockHash{};
+ CBlockIndex blockindex{};
- CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ TestBlockAndIndex()
+ {
+ CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
+ char a = '\0';
+ stream.write(&a, 1); // Prevent compaction
- CBlock block;
- stream >> block;
+ stream >> block;
- CBlockIndex blockindex;
- const uint256 blockHash = block.GetHash();
- blockindex.phashBlock = &blockHash;
- blockindex.nBits = 403014710;
+ blockHash = block.GetHash();
+ blockindex.phashBlock = &blockHash;
+ blockindex.nBits = 403014710;
+ }
+};
+} // namespace
+
+static void BlockToJsonVerbose(benchmark::Bench& bench)
+{
+ TestBlockAndIndex data;
bench.run([&] {
- (void)blockToJSON(block, &blockindex, &blockindex, /*verbose*/ true);
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ ankerl::nanobench::doNotOptimizeAway(univalue);
});
}
BENCHMARK(BlockToJsonVerbose);
+
+static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
+{
+ TestBlockAndIndex data;
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ bench.run([&] {
+ auto str = univalue.write();
+ ankerl::nanobench::doNotOptimizeAway(str);
+ });
+}
+
+BENCHMARK(BlockToJsonVerboseWrite);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index b385cec085..c96ef209e3 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -14,30 +14,24 @@
static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ 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(), "", CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
bool first_run;
if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
}
- auto handler = test_setup.m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
+ auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : 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));
- generatetoaddress(test_setup.m_node, ADDRESS_WATCHONLY);
+ generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
+ generatetoaddress(test_setup->m_node, ADDRESS_WATCHONLY);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 0830cb54cb..dc4b142f83 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -300,7 +300,6 @@ class NetinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr int8_t UNKNOWN_NETWORK{-1};
- static constexpr int8_t NET_I2P{3}; // pos of "i2p" in m_networks
static constexpr uint8_t m_networks_size{4};
static constexpr uint8_t MAX_DETAIL_LEVEL{4};
const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion", "i2p"}};
@@ -314,13 +313,11 @@ private:
}
return UNKNOWN_NETWORK;
}
- uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
- bool m_is_help_requested{false}; //!< Optional user-supplied arg to print help documentation
+ uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
bool m_is_asmap_on{false};
- bool m_is_i2p_on{false};
size_t m_max_addr_length{0};
size_t m_max_age_length{3};
size_t m_max_id_length{2};
@@ -367,68 +364,6 @@ private:
if (conn_type == "addr-fetch") return "addr";
return "";
}
- const UniValue NetinfoHelp()
- {
- return std::string{
- "-netinfo level|\"help\" \n\n"
- "Returns a network peer connections dashboard with information from the remote server.\n"
- "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
- "An optional integer argument from 0 to 4 can be passed for different peers listings.\n"
- "Pass \"help\" to see this detailed help documentation.\n"
- "If more than one argument is passed, only the first one is read and parsed.\n"
- "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
- "Arguments:\n"
- "1. level (integer 0-4, optional) Specify the info level of the peers dashboard (default 0):\n"
- " 0 - Connection counts and local addresses\n"
- " 1 - Like 0 but with a peers listing (without address or version columns)\n"
- " 2 - Like 1 but with an address column\n"
- " 3 - Like 1 but with a version column\n"
- " 4 - Like 1 but with both address and version columns\n"
- "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
- "Result:\n\n"
- "* The peers listing in levels 1-4 displays all of the peers sorted by direction and minimum ping time:\n\n"
- " Column Description\n"
- " ------ -----------\n"
- " <-> Direction\n"
- " \"in\" - inbound connections are those initiated by the peer\n"
- " \"out\" - outbound connections are those initiated by us\n"
- " type Type of peer connection\n"
- " \"full\" - full relay, the default\n"
- " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
- " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
- " \"feeler\" - short-lived connection for testing addresses\n"
- " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
- " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n"
- " mping Minimum observed ping time, in milliseconds (ms)\n"
- " ping Last observed ping time, in milliseconds (ms)\n"
- " send Time since last message sent to the peer, in seconds\n"
- " recv Time since last message received from the peer, in seconds\n"
- " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
- " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
- " hb High-bandwidth BIP152 compact block relay\n"
- " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
- " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
- " age Duration of connection to the peer, in minutes\n"
- " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
- " peer selection (only displayed if the -asmap config option is set)\n"
- " id Peer index, in increasing order of peer connections since node startup\n"
- " address IP address and port of the peer\n"
- " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
- "* The connection counts table displays the number of peers by direction, network, and the totals\n"
- " for each, as well as two special outbound columns for block relay peers and manual peers.\n\n"
- "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
- "Examples:\n\n"
- "Connection counts and local addresses only\n"
- "> bitcoin-cli -netinfo\n\n"
- "Compact peers listing\n"
- "> bitcoin-cli -netinfo 1\n\n"
- "Full dashboard\n"
- "> bitcoin-cli -netinfo 4\n\n"
- "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
- "> watch --interval 1 --no-title bitcoin-cli -netinfo 4\n\n"
- "See this help\n"
- "> bitcoin-cli -netinfo help\n"};
- }
const int64_t m_time_now{GetSystemTimeInSeconds()};
public:
@@ -441,10 +376,8 @@ public:
uint8_t n{0};
if (ParseUInt8(args.at(0), &n)) {
m_details_level = std::min(n, MAX_DETAIL_LEVEL);
- } else if (args.at(0) == "help") {
- m_is_help_requested = true;
} else {
- throw std::runtime_error(strprintf("invalid -netinfo argument: %s", args.at(0)));
+ throw std::runtime_error(strprintf("invalid -netinfo argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
}
}
UniValue result(UniValue::VARR);
@@ -455,9 +388,6 @@ public:
UniValue ProcessReply(const UniValue& batch_in) override
{
- if (m_is_help_requested) {
- return JSONRPCReplyObj(NetinfoHelp(), NullUniValue, 1);
- }
const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
@@ -472,7 +402,6 @@ public:
const std::string network{peer["network"].get_str()};
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
- m_is_i2p_on |= (network_id == NET_I2P);
const bool is_outbound{!peer["inbound"].get_bool()};
const bool is_block_relay{!peer["relaytxes"].get_bool()};
const std::string conn_type{peer["connection_type"].get_str()};
@@ -545,13 +474,14 @@ public:
// Report peer connection totals by type.
result += " ipv4 ipv6 onion";
- if (m_is_i2p_on) result += " i2p";
+ 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 += " total block";
if (m_manual_peers_count) result += " manual";
const std::array<std::string, 3> 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 (m_is_i2p_on) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
+ if (any_i2p_peers) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p 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);
@@ -576,6 +506,67 @@ public:
return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
}
+
+ const std::string m_help_doc{
+ "-netinfo level|\"help\" \n\n"
+ "Returns a network peer connections dashboard with information from the remote server.\n"
+ "This human-readable interface will change regularly and is not intended to be a stable API.\n"
+ "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
+ + strprintf("An optional integer argument from 0 to %d can be passed for different peers listings; %d to 255 are parsed as %d.\n", MAX_DETAIL_LEVEL, MAX_DETAIL_LEVEL, MAX_DETAIL_LEVEL) +
+ "Pass \"help\" to see this detailed help documentation.\n"
+ "If more than one argument is passed, only the first one is read and parsed.\n"
+ "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
+ "Arguments:\n"
+ + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", MAX_DETAIL_LEVEL) +
+ " 0 - Connection counts and local addresses\n"
+ " 1 - Like 0 but with a peers listing (without address or version columns)\n"
+ " 2 - Like 1 but with an address column\n"
+ " 3 - Like 1 but with a version column\n"
+ " 4 - Like 1 but with both address and version columns\n"
+ "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
+ "Result:\n\n"
+ + strprintf("* The peers listing in levels 1-%d displays all of the peers sorted by direction and minimum ping time:\n\n", MAX_DETAIL_LEVEL) +
+ " Column Description\n"
+ " ------ -----------\n"
+ " <-> Direction\n"
+ " \"in\" - inbound connections are those initiated by the peer\n"
+ " \"out\" - outbound connections are those initiated by us\n"
+ " type Type of peer connection\n"
+ " \"full\" - full relay, the default\n"
+ " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
+ " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
+ " \"feeler\" - short-lived connection for testing addresses\n"
+ " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
+ " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n"
+ " mping Minimum observed ping time, in milliseconds (ms)\n"
+ " ping Last observed ping time, in milliseconds (ms)\n"
+ " send Time since last message sent to the peer, in seconds\n"
+ " recv Time since last message received from the peer, in seconds\n"
+ " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
+ " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
+ " hb High-bandwidth BIP152 compact block relay\n"
+ " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
+ " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
+ " age Duration of connection to the peer, in minutes\n"
+ " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
+ " peer selection (only displayed if the -asmap config option is set)\n"
+ " id Peer index, in increasing order of peer connections since node startup\n"
+ " address IP address and port of the peer\n"
+ " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
+ "* The connection counts table displays the number of peers by direction, network, and the totals\n"
+ " for each, as well as two special outbound columns for block relay peers and manual peers.\n\n"
+ "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
+ "Examples:\n\n"
+ "Connection counts and local addresses only\n"
+ "> bitcoin-cli -netinfo\n\n"
+ "Compact peers listing\n"
+ "> bitcoin-cli -netinfo 1\n\n"
+ "Full dashboard\n"
+ + strprintf("> bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
+ "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
+ + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
+ "See this help\n"
+ "> bitcoin-cli -netinfo help\n"};
};
/** Process RPC generatetoaddress request. */
@@ -907,6 +898,10 @@ static int CommandLineRPC(int argc, char *argv[])
if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-netinfo", false)) {
+ if (!args.empty() && args.at(0) == "help") {
+ tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
+ return 0;
+ }
rh.reset(new NetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-generate", false)) {
const UniValue getnewaddress{GetNewAddress()};
diff --git a/src/compat.h b/src/compat.h
index dad14748a2..3449bc2661 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -44,6 +44,7 @@ typedef unsigned int SOCKET;
#define WSAEINVAL EINVAL
#define WSAEALREADY EALREADY
#define WSAEWOULDBLOCK EWOULDBLOCK
+#define WSAEAGAIN EAGAIN
#define WSAEMSGSIZE EMSGSIZE
#define WSAEINTR EINTR
#define WSAEINPROGRESS EINPROGRESS
@@ -51,6 +52,14 @@ typedef unsigned int SOCKET;
#define WSAENOTSOCK EBADF
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR -1
+#else
+#ifndef WSAEAGAIN
+#ifdef EAGAIN
+#define WSAEAGAIN EAGAIN
+#else
+#define WSAEAGAIN WSAEWOULDBLOCK
+#endif
+#endif
#endif
#ifdef WIN32
@@ -96,4 +105,14 @@ bool static inline IsSelectableSocket(const SOCKET& s) {
#endif
}
+// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+#endif
+
+// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
+#if !defined(MSG_DONTWAIT)
+#define MSG_DONTWAIT 0
+#endif
+
#endif // BITCOIN_COMPAT_H
diff --git a/src/core_io.h b/src/core_io.h
index 5469a760ee..01340ae2ee 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -40,7 +40,7 @@ std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strN
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
-UniValue ValueFromAmount(const CAmount& amount);
+UniValue ValueFromAmount(const CAmount amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type);
diff --git a/src/core_write.cpp b/src/core_write.cpp
index a3902863d6..d3034ae25d 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -14,17 +14,20 @@
#include <undo.h>
#include <univalue.h>
#include <util/check.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
-UniValue ValueFromAmount(const CAmount& amount)
+UniValue ValueFromAmount(const CAmount amount)
{
- bool sign = amount < 0;
- int64_t n_abs = (sign ? -amount : amount);
- int64_t quotient = n_abs / COIN;
- int64_t remainder = n_abs % COIN;
+ static_assert(COIN > 1);
+ int64_t quotient = amount / COIN;
+ int64_t remainder = amount % COIN;
+ if (amount < 0) {
+ quotient = -quotient;
+ remainder = -remainder;
+ }
return UniValue(UniValue::VNUM,
- strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
+ strprintf("%s%d.%08d", amount < 0 ? "-" : "", quotient, remainder));
}
std::string FormatScript(const CScript& script)
diff --git a/src/i2p.cpp b/src/i2p.cpp
new file mode 100644
index 0000000000..42270deaeb
--- /dev/null
+++ b/src/i2p.cpp
@@ -0,0 +1,407 @@
+// Copyright (c) 2020-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <compat.h>
+#include <compat/endian.h>
+#include <crypto/sha256.h>
+#include <fs.h>
+#include <i2p.h>
+#include <logging.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <random.h>
+#include <util/strencodings.h>
+#include <tinyformat.h>
+#include <util/readwritefile.h>
+#include <util/sock.h>
+#include <util/spanparsing.h>
+#include <util/system.h>
+
+#include <chrono>
+#include <stdexcept>
+#include <string>
+
+namespace i2p {
+
+/**
+ * Swap Standard Base64 <-> I2P Base64.
+ * Standard Base64 uses `+` and `/` as last two characters of its alphabet.
+ * I2P Base64 uses `-` and `~` respectively.
+ * So it is easy to detect in which one is the input and convert to the other.
+ * @param[in] from Input to convert.
+ * @return converted `from`
+ */
+static std::string SwapBase64(const std::string& from)
+{
+ std::string to;
+ to.resize(from.size());
+ for (size_t i = 0; i < from.size(); ++i) {
+ switch (from[i]) {
+ case '-':
+ to[i] = '+';
+ break;
+ case '~':
+ to[i] = '/';
+ break;
+ case '+':
+ to[i] = '-';
+ break;
+ case '/':
+ to[i] = '~';
+ break;
+ default:
+ to[i] = from[i];
+ break;
+ }
+ }
+ return to;
+}
+
+/**
+ * Decode an I2P-style Base64 string.
+ * @param[in] i2p_b64 I2P-style Base64 string.
+ * @return decoded `i2p_b64`
+ * @throw std::runtime_error if decoding fails
+ */
+static Binary DecodeI2PBase64(const std::string& i2p_b64)
+{
+ const std::string& std_b64 = SwapBase64(i2p_b64);
+ bool invalid;
+ Binary decoded = DecodeBase64(std_b64.c_str(), &invalid);
+ if (invalid) {
+ throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
+ }
+ return decoded;
+}
+
+/**
+ * Derive the .b32.i2p address of an I2P destination (binary).
+ * @param[in] dest I2P destination.
+ * @return the address that corresponds to `dest`
+ * @throw std::runtime_error if conversion fails
+ */
+static CNetAddr DestBinToAddr(const Binary& dest)
+{
+ CSHA256 hasher;
+ hasher.Write(dest.data(), dest.size());
+ unsigned char hash[CSHA256::OUTPUT_SIZE];
+ hasher.Finalize(hash);
+
+ CNetAddr addr;
+ const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
+ if (!addr.SetSpecial(addr_str)) {
+ throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
+ }
+
+ return addr;
+}
+
+/**
+ * Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
+ * @param[in] dest I2P destination.
+ * @return the address that corresponds to `dest`
+ * @throw std::runtime_error if conversion fails
+ */
+static CNetAddr DestB64ToAddr(const std::string& dest)
+{
+ const Binary& decoded = DecodeI2PBase64(dest);
+ return DestBinToAddr(decoded);
+}
+
+namespace sam {
+
+Session::Session(const fs::path& private_key_file,
+ const CService& control_host,
+ CThreadInterrupt* interrupt)
+ : m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt)
+{
+}
+
+Session::~Session()
+{
+ LOCK(m_mutex);
+ Disconnect();
+}
+
+bool Session::Listen(Connection& conn)
+{
+ try {
+ LOCK(m_mutex);
+ CreateIfNotCreatedAlready();
+ conn.me = m_my_addr;
+ conn.sock = StreamAccept();
+ return true;
+ } catch (const std::runtime_error& e) {
+ Log("Error listening: %s", e.what());
+ CheckControlSock();
+ }
+ return false;
+}
+
+bool Session::Accept(Connection& conn)
+{
+ try {
+ while (!*m_interrupt) {
+ Sock::Event occurred;
+ conn.sock.Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred);
+
+ if ((occurred & Sock::RECV) == 0) {
+ // Timeout, no incoming connections within MAX_WAIT_FOR_IO.
+ continue;
+ }
+
+ const std::string& peer_dest =
+ conn.sock.RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt);
+
+ conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
+
+ return true;
+ }
+ } catch (const std::runtime_error& e) {
+ Log("Error accepting: %s", e.what());
+ CheckControlSock();
+ }
+ return false;
+}
+
+bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
+{
+ proxy_error = true;
+
+ std::string session_id;
+ Sock sock;
+ conn.peer = to;
+
+ try {
+ {
+ LOCK(m_mutex);
+ CreateIfNotCreatedAlready();
+ session_id = m_session_id;
+ conn.me = m_my_addr;
+ sock = Hello();
+ }
+
+ const Reply& lookup_reply =
+ SendRequestAndGetReply(sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
+
+ const std::string& dest = lookup_reply.Get("VALUE");
+
+ const Reply& connect_reply = SendRequestAndGetReply(
+ sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
+ false);
+
+ const std::string& result = connect_reply.Get("RESULT");
+
+ if (result == "OK") {
+ conn.sock = std::move(sock);
+ return true;
+ }
+
+ if (result == "INVALID_ID") {
+ LOCK(m_mutex);
+ Disconnect();
+ throw std::runtime_error("Invalid session id");
+ }
+
+ if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
+ proxy_error = false;
+ }
+
+ throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
+ } catch (const std::runtime_error& e) {
+ Log("Error connecting to %s: %s", to.ToString(), e.what());
+ CheckControlSock();
+ return false;
+ }
+}
+
+// Private methods
+
+std::string Session::Reply::Get(const std::string& key) const
+{
+ const auto& pos = keys.find(key);
+ if (pos == keys.end() || !pos->second.has_value()) {
+ throw std::runtime_error(
+ strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
+ }
+ return pos->second.value();
+}
+
+template <typename... Args>
+void Session::Log(const std::string& fmt, const Args&... args) const
+{
+ LogPrint(BCLog::I2P, "I2P: %s\n", tfm::format(fmt, args...));
+}
+
+Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
+ const std::string& request,
+ bool check_result_ok) const
+{
+ sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);
+
+ Reply reply;
+
+ // Don't log the full "SESSION CREATE ..." because it contains our private key.
+ reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request;
+
+ // It could take a few minutes for the I2P router to reply as it is querying the I2P network
+ // (when doing name lookup, for example). Notice: `RecvUntilTerminator()` is checking
+ // `m_interrupt` more often, so we would not be stuck here for long if `m_interrupt` is
+ // signaled.
+ static constexpr auto recv_timeout = 3min;
+
+ reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt);
+
+ for (const auto& kv : spanparsing::Split(reply.full, ' ')) {
+ const auto& pos = std::find(kv.begin(), kv.end(), '=');
+ if (pos != kv.end()) {
+ reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
+ } else {
+ reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
+ }
+ }
+
+ if (check_result_ok && reply.Get("RESULT") != "OK") {
+ throw std::runtime_error(
+ strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
+ }
+
+ return reply;
+}
+
+Sock Session::Hello() const
+{
+ auto sock = CreateSock(m_control_host);
+
+ if (!sock) {
+ throw std::runtime_error("Cannot create socket");
+ }
+
+ if (!ConnectSocketDirectly(m_control_host, sock->Get(), nConnectTimeout, true)) {
+ throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
+ }
+
+ SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
+
+ return std::move(*sock);
+}
+
+void Session::CheckControlSock()
+{
+ LOCK(m_mutex);
+
+ std::string errmsg;
+ if (!m_control_sock.IsConnected(errmsg)) {
+ Log("Control socket error: %s", errmsg);
+ Disconnect();
+ }
+}
+
+void Session::DestGenerate(const Sock& sock)
+{
+ // https://geti2p.net/spec/common-structures#key-certificates
+ // "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
+ // Use "7" because i2pd <2.24.0 does not recognize the textual form.
+ const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
+
+ m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
+}
+
+void Session::GenerateAndSavePrivateKey(const Sock& sock)
+{
+ DestGenerate(sock);
+
+ // umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
+ if (!WriteBinaryFile(m_private_key_file,
+ std::string(m_private_key.begin(), m_private_key.end()))) {
+ throw std::runtime_error(
+ strprintf("Cannot save I2P private key to %s", m_private_key_file));
+ }
+}
+
+Binary Session::MyDestination() const
+{
+ // From https://geti2p.net/spec/common-structures#destination:
+ // "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
+ // non-zero"
+ static constexpr size_t DEST_LEN_BASE = 387;
+ static constexpr size_t CERT_LEN_POS = 385;
+
+ uint16_t cert_len;
+ memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
+ cert_len = be16toh(cert_len);
+
+ const size_t dest_len = DEST_LEN_BASE + cert_len;
+
+ return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
+}
+
+void Session::CreateIfNotCreatedAlready()
+{
+ std::string errmsg;
+ if (m_control_sock.IsConnected(errmsg)) {
+ return;
+ }
+
+ Log("Creating SAM session with %s", m_control_host.ToString());
+
+ Sock sock = Hello();
+
+ const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
+ if (read_ok) {
+ m_private_key.assign(data.begin(), data.end());
+ } else {
+ GenerateAndSavePrivateKey(sock);
+ }
+
+ const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
+ const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
+
+ SendRequestAndGetReply(sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
+ session_id, private_key_b64));
+
+ m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
+ m_session_id = session_id;
+ m_control_sock = std::move(sock);
+
+ LogPrintf("I2P: SAM session created: session id=%s, my address=%s\n", m_session_id,
+ m_my_addr.ToString());
+}
+
+Sock Session::StreamAccept()
+{
+ Sock sock = Hello();
+
+ const Reply& reply = SendRequestAndGetReply(
+ sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
+
+ const std::string& result = reply.Get("RESULT");
+
+ if (result == "OK") {
+ return sock;
+ }
+
+ if (result == "INVALID_ID") {
+ // If our session id is invalid, then force session re-creation on next usage.
+ Disconnect();
+ }
+
+ throw std::runtime_error(strprintf("\"%s\"", reply.full));
+}
+
+void Session::Disconnect()
+{
+ if (m_control_sock.Get() != INVALID_SOCKET) {
+ if (m_session_id.empty()) {
+ Log("Destroying incomplete session");
+ } else {
+ Log("Destroying session %s", m_session_id);
+ }
+ }
+ m_control_sock.Reset();
+ m_session_id.clear();
+}
+} // namespace sam
+} // namespace i2p
diff --git a/src/i2p.h b/src/i2p.h
new file mode 100644
index 0000000000..8fafe0a4d0
--- /dev/null
+++ b/src/i2p.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2020-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_I2P_H
+#define BITCOIN_I2P_H
+
+#include <compat.h>
+#include <fs.h>
+#include <netaddress.h>
+#include <sync.h>
+#include <threadinterrupt.h>
+#include <util/sock.h>
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace i2p {
+
+/**
+ * Binary data.
+ */
+using Binary = std::vector<uint8_t>;
+
+/**
+ * An established connection with another peer.
+ */
+struct Connection {
+ /** Connected socket. */
+ Sock sock;
+
+ /** Our I2P address. */
+ CService me;
+
+ /** The peer's I2P address. */
+ CService peer;
+};
+
+namespace sam {
+
+/**
+ * I2P SAM session.
+ */
+class Session
+{
+public:
+ /**
+ * Construct a session. This will not initiate any IO, the session will be lazily created
+ * later when first used.
+ * @param[in] private_key_file Path to a private key file. If the file does not exist then the
+ * private key will be generated and saved into the file.
+ * @param[in] control_host Location of the SAM proxy.
+ * @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
+ * possible and executing methods throw an exception. Notice: only a pointer to the
+ * `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
+ * `Session` object.
+ */
+ Session(const fs::path& private_key_file,
+ const CService& control_host,
+ CThreadInterrupt* interrupt);
+
+ /**
+ * Destroy the session, closing the internally used sockets. The sockets that have been
+ * returned by `Accept()` or `Connect()` will not be closed, but they will be closed by
+ * the SAM proxy because the session is destroyed. So they will return an error next time
+ * we try to read or write to them.
+ */
+ ~Session();
+
+ /**
+ * Start listening for an incoming connection.
+ * @param[out] conn Upon successful completion the `sock` and `me` members will be set
+ * to the listening socket and address.
+ * @return true on success
+ */
+ bool Listen(Connection& conn);
+
+ /**
+ * Wait for and accept a new incoming connection.
+ * @param[in,out] conn The `sock` member is used for waiting and accepting. Upon successful
+ * completion the `peer` member will be set to the address of the incoming peer.
+ * @return true on success
+ */
+ bool Accept(Connection& conn);
+
+ /**
+ * Connect to an I2P peer.
+ * @param[in] to Peer to connect to.
+ * @param[out] conn Established connection. Only set if `true` is returned.
+ * @param[out] proxy_error If an error occurs due to proxy or general network failure, then
+ * this is set to `true`. If an error occurs due to unreachable peer (likely peer is down), then
+ * it is set to `false`. Only set if `false` is returned.
+ * @return true on success
+ */
+ bool Connect(const CService& to, Connection& conn, bool& proxy_error);
+
+private:
+ /**
+ * A reply from the SAM proxy.
+ */
+ struct Reply {
+ /**
+ * Full, unparsed reply.
+ */
+ std::string full;
+
+ /**
+ * Request, used for detailed error reporting.
+ */
+ std::string request;
+
+ /**
+ * A map of keywords from the parsed reply.
+ * For example, if the reply is "A=X B C=YZ", then the map will be
+ * keys["A"] == "X"
+ * keys["B"] == (empty std::optional)
+ * keys["C"] == "YZ"
+ */
+ std::unordered_map<std::string, std::optional<std::string>> keys;
+
+ /**
+ * Get the value of a given key.
+ * For example if the reply is "A=X B" then:
+ * Value("A") -> "X"
+ * Value("B") -> throws
+ * Value("C") -> throws
+ * @param[in] key Key whose value to retrieve
+ * @returns the key's value
+ * @throws std::runtime_error if the key is not present or if it has no value
+ */
+ std::string Get(const std::string& key) const;
+ };
+
+ /**
+ * Log a message in the `BCLog::I2P` category.
+ * @param[in] fmt printf(3)-like format string.
+ * @param[in] args printf(3)-like arguments that correspond to `fmt`.
+ */
+ template <typename... Args>
+ void Log(const std::string& fmt, const Args&... args) const;
+
+ /**
+ * Send request and get a reply from the SAM proxy.
+ * @param[in] sock A socket that is connected to the SAM proxy.
+ * @param[in] request Raw request to send, a newline terminator is appended to it.
+ * @param[in] check_result_ok If true then after receiving the reply a check is made
+ * whether it contains "RESULT=OK" and an exception is thrown if it does not.
+ * @throws std::runtime_error if an error occurs
+ */
+ Reply SendRequestAndGetReply(const Sock& sock,
+ const std::string& request,
+ bool check_result_ok = true) const;
+
+ /**
+ * Open a new connection to the SAM proxy.
+ * @return a connected socket
+ * @throws std::runtime_error if an error occurs
+ */
+ Sock Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Check the control socket for errors and possibly disconnect.
+ */
+ void CheckControlSock();
+
+ /**
+ * Generate a new destination with the SAM proxy and set `m_private_key` to it.
+ * @param[in] sock Socket to use for talking to the SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Generate a new destination with the SAM proxy, set `m_private_key` to it and save
+ * it on disk to `m_private_key_file`.
+ * @param[in] sock Socket to use for talking to the SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Derive own destination from `m_private_key`.
+ * @see https://geti2p.net/spec/common-structures#destination
+ * @return an I2P destination
+ */
+ Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Create the session if not already created. Reads the private key file and connects to the
+ * SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
+ * session id. Return the idle socket that is waiting for a peer to connect to us.
+ * @throws std::runtime_error if an error occurs
+ */
+ Sock StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Destroy the session, closing the internally used sockets.
+ */
+ void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * The name of the file where this peer's private key is stored (in binary).
+ */
+ const fs::path m_private_key_file;
+
+ /**
+ * The host and port of the SAM control service.
+ */
+ const CService m_control_host;
+
+ /**
+ * Cease network activity when this is signaled.
+ */
+ CThreadInterrupt* const m_interrupt;
+
+ /**
+ * Mutex protecting the members that can be concurrently accessed.
+ */
+ mutable Mutex m_mutex;
+
+ /**
+ * The private key of this peer.
+ * @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3
+ */
+ Binary m_private_key GUARDED_BY(m_mutex);
+
+ /**
+ * SAM control socket.
+ * Used to connect to the I2P SAM service and create a session
+ * ("SESSION CREATE"). With the established session id we later open
+ * other connections to the SAM service to accept incoming I2P
+ * connections and make outgoing ones.
+ * See https://geti2p.net/en/docs/api/samv3
+ */
+ Sock m_control_sock GUARDED_BY(m_mutex);
+
+ /**
+ * Our .b32.i2p address.
+ * Derived from `m_private_key`.
+ */
+ CService m_my_addr GUARDED_BY(m_mutex);
+
+ /**
+ * SAM session id.
+ */
+ std::string m_session_id GUARDED_BY(m_mutex);
+};
+
+} // namespace sam
+} // namespace i2p
+
+#endif /* BITCOIN_I2P_H */
diff --git a/src/init.cpp b/src/init.cpp
index 8adb637d6e..584b148aff 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -52,6 +52,7 @@
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
+#include <txorphanage.h>
#include <util/asmap.h>
#include <util/check.h>
#include <util/moneystr.h>
@@ -447,7 +448,9 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
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("-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 ipv4 or ipv6 but not onion 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("-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("-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);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -718,7 +721,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
- LoadGenesisBlock(chainparams);
+ ::ChainstateActive().LoadGenesisBlock(chainparams);
}
// -loadblock=
@@ -848,6 +851,9 @@ void InitParameterInteraction(ArgsManager& args)
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (args.SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
+ if (args.SoftSetBoolArg("-i2pacceptincoming", false)) {
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -i2pacceptincoming=0\n", __func__);
+ }
}
if (args.IsArgSet("-externalip")) {
@@ -1631,7 +1637,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1742,7 +1748,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// work when we allow VerifyDB to be parameterized by chainstate.
if (&::ChainstateActive() == chainstate &&
!CVerifyDB().VerifyDB(
- chainparams, &chainstate->CoinsDB(),
+ chainparams, *chainstate, &chainstate->CoinsDB(),
args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
@@ -1991,6 +1997,21 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
connOptions.m_specified_outgoing = connect;
}
}
+
+ const std::string& i2psam_arg = args.GetArg("-i2psam", "");
+ if (!i2psam_arg.empty()) {
+ CService addr;
+ if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
+ return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
+ }
+ SetReachable(NET_I2P, true);
+ SetProxy(NET_I2P, proxyType{addr});
+ } else {
+ SetReachable(NET_I2P, false);
+ }
+
+ connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", true);
+
if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 1a49518d69..ebc5466173 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -159,6 +159,9 @@ public:
//! Check if transaction is RBF opt in.
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
+ //! Check if transaction is in mempool.
+ virtual bool isInMempool(const uint256& txid) = 0;
+
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 15f7ef6256..1dd1e92e2f 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -6,9 +6,10 @@
#define BITCOIN_INTERFACES_NODE_H
#include <amount.h> // For CAmount
-#include <net.h> // For CConnman::NumConnections
+#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
+#include <netbase.h> // For ConnectionDirection
#include <support/allocators/secure.h> // For SecureString
#include <util/translation.h>
@@ -88,7 +89,7 @@ public:
virtual bool getProxy(Network net, proxyType& proxy_info) = 0;
//! Get number of connections.
- virtual size_t getNodeCount(CConnman::NumConnections flags) = 0;
+ virtual size_t getNodeCount(ConnectionDirection flags) = 0;
//! Get stats for connected nodes.
using NodesStats = std::vector<std::tuple<CNodeStats, bool, CNodeStateStats>>;
diff --git a/src/logging.cpp b/src/logging.cpp
index e82f2c2810..866213786e 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -156,6 +156,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::QT, "qt"},
{BCLog::LEVELDB, "leveldb"},
{BCLog::VALIDATION, "validation"},
+ {BCLog::I2P, "i2p"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
diff --git a/src/logging.h b/src/logging.h
index 4ece8f5e3a..436f0cd12e 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -57,6 +57,7 @@ namespace BCLog {
QT = (1 << 19),
LEVELDB = (1 << 20),
VALIDATION = (1 << 21),
+ I2P = (1 << 22),
ALL = ~(uint32_t)0,
};
diff --git a/src/miner.cpp b/src/miner.cpp
index 076d43c951..fbaef0f224 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -39,13 +39,13 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
return nNewTime - nOldTime;
}
-void RegenerateCommitments(CBlock& block)
+void RegenerateCommitments(CBlock& block, BlockManager& blockman)
{
CMutableTransaction tx{*block.vtx.at(0)};
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
+ GenerateCoinbaseCommitment(block, WITH_LOCK(::cs_main, assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); return blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
}
@@ -99,7 +99,7 @@ void BlockAssembler::resetBlock()
Optional<int64_t> BlockAssembler::m_last_block_num_txs{nullopt};
Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt};
-std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
+std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn)
{
int64_t nTimeStart = GetTimeMicros();
@@ -117,7 +117,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- CBlockIndex* pindexPrev = ::ChainActive().Tip();
+ assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*chainstate.m_chain.Tip()));
+ CBlockIndex* pindexPrev = chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -176,7 +177,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+ if (!TestBlockValidity(state, chainparams, chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
int64_t nTime2 = GetTimeMicros();
diff --git a/src/miner.h b/src/miner.h
index 9a2b7063f4..a67fec6dd8 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -158,7 +158,7 @@ public:
explicit BlockAssembler(const CTxMemPool& mempool, const CChainParams& params, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
- std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
+ std::unique_ptr<CBlockTemplate> CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn);
static Optional<int64_t> m_last_block_num_txs;
static Optional<int64_t> m_last_block_weight;
@@ -202,6 +202,6 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
-void RegenerateCommitments(CBlock& block);
+void RegenerateCommitments(CBlock& block, BlockManager& blockman);
#endif // BITCOIN_MINER_H
diff --git a/src/net.cpp b/src/net.cpp
index d9571e7036..cf7d3e50ba 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -11,8 +11,10 @@
#include <banman.h>
#include <clientversion.h>
+#include <compat.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
+#include <i2p.h>
#include <net_permissions.h>
#include <netbase.h>
#include <node/ui_interface.h>
@@ -72,16 +74,6 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
-// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif
-
-// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_DONTWAIT)
-#define MSG_DONTWAIT 0
-#endif
-
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
@@ -430,10 +422,20 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
bool connected = false;
std::unique_ptr<Sock> sock;
proxyType proxy;
+ CAddress addr_bind;
+ assert(!addr_bind.IsValid());
+
if (addrConnect.IsValid()) {
bool proxyConnectionFailed = false;
- if (GetProxy(addrConnect.GetNetwork(), proxy)) {
+ if (addrConnect.GetNetwork() == NET_I2P && m_i2p_sam_session.get() != nullptr) {
+ i2p::Connection conn;
+ if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
+ connected = true;
+ sock = std::make_unique<Sock>(std::move(conn.sock));
+ addr_bind = CAddress{conn.me, NODE_NONE};
+ }
+ } else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
sock = CreateSock(proxy.proxy);
if (!sock) {
return nullptr;
@@ -473,7 +475,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(sock->Get());
+ if (!addr_bind.IsValid()) {
+ addr_bind = GetBindAddress(sock->Get());
+ }
CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
pnode->AddRef();
@@ -599,8 +603,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
stats.minFeeFilter = 0;
}
- stats.m_ping_usec = m_last_ping_time;
- stats.m_min_ping_usec = m_min_ping_time;
+ X(m_last_ping_time);
+ X(m_min_ping_time);
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -1005,17 +1009,35 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
- int nInbound = 0;
- int nMaxInbound = nMaxConnections - m_max_outbound;
- if (hSocket != INVALID_SOCKET) {
- if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ if (hSocket == INVALID_SOCKET) {
+ const int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK) {
+ LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
}
+ return;
+ }
+
+ if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
+ LogPrintf("Warning: Unknown socket family\n");
}
+ const CAddress addr_bind = GetBindAddress(hSocket);
+
NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
+
+ CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr);
+}
+
+void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ NetPermissionFlags permissionFlags,
+ const CAddress& addr_bind,
+ const CAddress& addr)
+{
+ int nInbound = 0;
+ int nMaxInbound = nMaxConnections - m_max_outbound;
+
AddWhitelistPermissionFlags(permissionFlags, addr);
if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
@@ -1032,14 +1054,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- if (hSocket == INVALID_SOCKET)
- {
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK)
- LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
- return;
- }
-
if (!fNetworkActive) {
LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString());
CloseSocket(hSocket);
@@ -1087,7 +1101,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
ServiceFlags nodeServices = nLocalServices;
if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
@@ -1748,12 +1761,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Initiate network connections
- auto start = GetTime<std::chrono::seconds>();
+ auto start = GetTime<std::chrono::microseconds>();
// Minimum time before next feeler connection (in microseconds).
-
- int64_t nNextFeeler = PoissonNextSend(count_microseconds(start), FEELER_INTERVAL);
- int64_t nNextExtraBlockRelay = PoissonNextSend(count_microseconds(start), EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ auto next_feeler = PoissonNextSend(start, FEELER_INTERVAL);
+ auto next_extra_block_relay = PoissonNextSend(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
@@ -1836,7 +1848,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
- int64_t nTime = GetTimeMicros();
+ auto now = GetTime<std::chrono::microseconds>();
bool anchor = false;
bool fFeeler = false;
@@ -1848,7 +1860,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
// try opening an additional OUTBOUND_FULL_RELAY connection. If none of
// these conditions are met, check to see if it's time to try an extra
- // block-relay-only peer (to confirm our tip is current, see below) or the nNextFeeler
+ // block-relay-only peer (to confirm our tip is current, see below) or the next_feeler
// timer to decide if we should open a FEELER.
if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) {
@@ -1860,7 +1872,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
conn_type = ConnectionType::BLOCK_RELAY;
} else if (GetTryNewOutboundPeer()) {
// OUTBOUND_FULL_RELAY
- } else if (nTime > nNextExtraBlockRelay && m_start_extra_block_relay_peers) {
+ } else if (now > next_extra_block_relay && m_start_extra_block_relay_peers) {
// Periodically connect to a peer (using regular outbound selection
// methodology from addrman) and stay connected long enough to sync
// headers, but not much else.
@@ -1882,10 +1894,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Because we can promote these connections to block-relay-only
// connections, they do not get their own ConnectionType enum
// (similar to how we deal with extra outbound peers).
- nNextExtraBlockRelay = PoissonNextSend(nTime, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ next_extra_block_relay = PoissonNextSend(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
conn_type = ConnectionType::BLOCK_RELAY;
- } else if (nTime > nNextFeeler) {
- nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ } else if (now > next_feeler) {
+ next_feeler = PoissonNextSend(now, FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
fFeeler = true;
} else {
@@ -2175,6 +2187,45 @@ void CConnman::ThreadMessageHandler()
}
}
+void CConnman::ThreadI2PAcceptIncoming()
+{
+ static constexpr auto err_wait_begin = 1s;
+ static constexpr auto err_wait_cap = 5min;
+ auto err_wait = err_wait_begin;
+
+ bool advertising_listen_addr = false;
+ i2p::Connection conn;
+
+ while (!interruptNet) {
+
+ if (!m_i2p_sam_session->Listen(conn)) {
+ if (advertising_listen_addr && conn.me.IsValid()) {
+ RemoveLocal(conn.me);
+ advertising_listen_addr = false;
+ }
+
+ interruptNet.sleep_for(err_wait);
+ if (err_wait < err_wait_cap) {
+ err_wait *= 2;
+ }
+
+ continue;
+ }
+
+ if (!advertising_listen_addr) {
+ AddLocal(conn.me, LOCAL_BIND);
+ advertising_listen_addr = true;
+ }
+
+ if (!m_i2p_sam_session->Accept(conn)) {
+ continue;
+ }
+
+ CreateNodeFromAcceptedSocket(conn.sock.Release(), NetPermissionFlags::PF_NONE,
+ CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
+ }
+}
+
bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions)
{
int nOne = 1;
@@ -2374,6 +2425,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
return false;
}
+ proxyType i2p_sam;
+ if (GetProxy(NET_I2P, i2p_sam)) {
+ m_i2p_sam_session = std::make_unique<i2p::sam::Session>(GetDataDir() / "i2p_private_key",
+ i2p_sam.proxy, &interruptNet);
+ }
+
for (const auto& strDest : connOptions.vSeedNodes) {
AddAddrFetch(strDest);
}
@@ -2454,6 +2511,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Process messages
threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
+ if (connOptions.m_i2p_accept_incoming && m_i2p_sam_session.get() != nullptr) {
+ threadI2PAcceptIncoming =
+ std::thread(&TraceThread<std::function<void()>>, "i2paccept",
+ std::function<void()>(std::bind(&CConnman::ThreadI2PAcceptIncoming, this)));
+ }
+
// Dump network addresses
scheduler.scheduleEvery([this] { DumpAddresses(); }, DUMP_PEERS_INTERVAL);
@@ -2501,6 +2564,9 @@ void CConnman::Interrupt()
void CConnman::StopThreads()
{
+ if (threadI2PAcceptIncoming.joinable()) {
+ threadI2PAcceptIncoming.join();
+ }
if (threadMessageHandler.joinable())
threadMessageHandler.join();
if (threadOpenConnections.joinable())
@@ -2597,9 +2663,7 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
- SOCKET socket;
- WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
- auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
+ auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
.Write(requestor.addr.GetNetwork())
.Write(local_socket_bytes.data(), local_socket_bytes.size())
@@ -2661,15 +2725,15 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
return false;
}
-size_t CConnman::GetNodeCount(NumConnections flags)
+size_t CConnman::GetNodeCount(ConnectionDirection flags)
{
LOCK(cs_vNodes);
- if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total
+ if (flags == ConnectionDirection::Both) // Shortcut if we want total
return vNodes.size();
int nNum = 0;
for (const auto& pnode : vNodes) {
- if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
+ if (flags & (pnode->IsInboundConn() ? ConnectionDirection::In : ConnectionDirection::Out)) {
nNum++;
}
}
@@ -2918,20 +2982,21 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
+std::chrono::microseconds CConnman::PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- if (m_next_send_inv_to_incoming < now) {
+ if (m_next_send_inv_to_incoming.load() < now) {
// If this function were called from multiple threads simultaneously
// it would possible that both update the next send variable, and return a different result to their caller.
// This is not possible in practice as only the net processing thread invokes this function.
- m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
+ m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval);
}
return m_next_send_inv_to_incoming;
}
-int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
+std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+ double unscaled = -log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
+ return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
}
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
diff --git a/src/net.h b/src/net.h
index 67d1cf0e55..beef47f045 100644
--- a/src/net.h
+++ b/src/net.h
@@ -14,8 +14,10 @@
#include <compat.h>
#include <crypto/siphash.h>
#include <hash.h>
+#include <i2p.h>
#include <net_permissions.h>
#include <netaddress.h>
+#include <netbase.h>
#include <optional.h>
#include <policy/feerate.h>
#include <protocol.h>
@@ -48,10 +50,10 @@ static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60;
-/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
-static const int FEELER_INTERVAL = 120;
+/** Run the feeler connection loop once every 2 minutes. **/
+static constexpr auto FEELER_INTERVAL = 2min;
/** Run the extra block-relay-only connection loop once every 5 minutes. **/
-static const int EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 300;
+static constexpr auto EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 5min;
/** The maximum number of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
@@ -260,8 +262,8 @@ public:
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
NetPermissionFlags m_permissionFlags;
- int64_t m_ping_usec;
- int64_t m_min_ping_usec;
+ std::chrono::microseconds m_last_ping_time;
+ std::chrono::microseconds m_min_ping_time;
CAmount minFeeFilter;
// Our address, as reported by the peer
std::string addrLocal;
@@ -572,7 +574,7 @@ public:
/** Minimum fee rate with which to filter inv's to this node */
std::atomic<CAmount> minFeeFilter{0};
CAmount lastSentFeeFilter{0};
- int64_t nextSendTimeFeeFilter{0};
+ std::chrono::microseconds m_next_send_feefilter{0};
};
// m_tx_relay == nullptr if we're not relaying transactions with this peer
@@ -592,11 +594,11 @@ public:
std::atomic<int64_t> nLastTXTime{0};
/** Last measured round-trip time. Used only for RPC/GUI stats/debugging.*/
- std::atomic<int64_t> m_last_ping_time{0};
+ std::atomic<std::chrono::microseconds> m_last_ping_time{0us};
/** Lowest measured round-trip time. Used as an inbound peer eviction
* criterium in CConnman::AttemptToEvictConnection. */
- std::atomic<int64_t> m_min_ping_time{std::numeric_limits<int64_t>::max()};
+ std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};
CNode(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
~CNode();
@@ -718,8 +720,8 @@ public:
/** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */
void PongReceived(std::chrono::microseconds ping_time) {
- m_last_ping_time = count_microseconds(ping_time);
- m_min_ping_time = std::min(m_min_ping_time.load(), count_microseconds(ping_time));
+ m_last_ping_time = ping_time;
+ m_min_ping_time = std::min(m_min_ping_time.load(), ping_time);
}
private:
@@ -800,13 +802,6 @@ class CConnman
{
public:
- enum NumConnections {
- CONNECTIONS_NONE = 0,
- CONNECTIONS_IN = (1U << 0),
- CONNECTIONS_OUT = (1U << 1),
- CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
- };
-
struct Options
{
ServiceFlags nLocalServices = NODE_NONE;
@@ -831,6 +826,7 @@ public:
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
std::vector<bool> m_asmap;
+ bool m_i2p_accept_incoming;
};
void Init(const Options& connOptions) {
@@ -974,7 +970,7 @@ public:
*/
bool AddConnection(const std::string& address, ConnectionType conn_type);
- size_t GetNodeCount(NumConnections num);
+ size_t GetNodeCount(ConnectionDirection);
void GetNodeStats(std::vector<CNodeStats>& vstats);
bool DisconnectNode(const std::string& node);
bool DisconnectNode(const CSubNet& subnet);
@@ -1019,7 +1015,7 @@ public:
Works assuming that a single interval is used.
Variable intervals will result in privacy decrease.
*/
- int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
+ std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
@@ -1048,7 +1044,22 @@ private:
void ProcessAddrFetch();
void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
+ void ThreadI2PAcceptIncoming();
void AcceptConnection(const ListenSocket& hListenSocket);
+
+ /**
+ * Create a `CNode` object from a socket that has just been accepted and add the node to
+ * the `vNodes` member.
+ * @param[in] hSocket Connected socket to communicate with the peer.
+ * @param[in] permissionFlags The peer's permissions.
+ * @param[in] addr_bind The address and port at our side of the connection.
+ * @param[in] addr The address and port at the peer's side of the connection.
+ */
+ void CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ NetPermissionFlags permissionFlags,
+ const CAddress& addr_bind,
+ const CAddress& addr);
+
void DisconnectNodes();
void NotifyNumConnectionsChanged();
/** Return true if the peer is inactive and should be disconnected. */
@@ -1207,13 +1218,26 @@ private:
Mutex mutexMsgProc;
std::atomic<bool> flagInterruptMsgProc{false};
+ /**
+ * This is signaled when network activity should cease.
+ * A pointer to it is saved in `m_i2p_sam_session`, so make sure that
+ * the lifetime of `interruptNet` is not shorter than
+ * the lifetime of `m_i2p_sam_session`.
+ */
CThreadInterrupt interruptNet;
+ /**
+ * I2P SAM session.
+ * Used to accept incoming and make outgoing I2P connections.
+ */
+ std::unique_ptr<i2p::sam::Session> m_i2p_sam_session;
+
std::thread threadDNSAddressSeed;
std::thread threadSocketHandler;
std::thread threadOpenAddedConnections;
std::thread threadOpenConnections;
std::thread threadMessageHandler;
+ std::thread threadI2PAcceptIncoming;
/** flag for deciding to connect to an extra outbound peer,
* in excess of m_max_outbound_full_relay
@@ -1226,7 +1250,7 @@ private:
*/
std::atomic_bool m_start_extra_block_relay_peers{false};
- std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+ std::atomic<std::chrono::microseconds> m_next_send_inv_to_incoming{0us};
/**
* A vector of -bind=<address>:<port>=onion arguments each of which is
@@ -1239,13 +1263,7 @@ private:
};
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
-int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
-
-/** Wrapper to return mockable type */
-inline std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
-{
- return std::chrono::microseconds{PoissonNextSend(now.count(), average_interval.count())};
-}
+std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval);
/** Dump binary message to file, with timestamp */
void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming);
@@ -1254,7 +1272,7 @@ struct NodeEvictionCandidate
{
NodeId id;
int64_t nTimeConnected;
- int64_t m_min_ping_time;
+ std::chrono::microseconds m_min_ping_time;
int64_t nLastBlockTime;
int64_t nLastTXTime;
bool fRelevantServices;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index c97f7ced46..01b55d8961 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -26,6 +26,7 @@
#include <streams.h>
#include <tinyformat.h>
#include <txmempool.h>
+#include <txorphanage.h>
#include <txrequest.h>
#include <util/check.h> // For NDEBUG compile time check
#include <util/strencodings.h>
@@ -35,18 +36,14 @@
#include <memory>
#include <typeinfo>
-/** Expiration time for orphan transactions in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** How long to cache transactions in mapRelay for normal relay */
-static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME = std::chrono::minutes{15};
+static constexpr auto RELAY_TX_CACHE_TIME = 15min;
/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
-static constexpr std::chrono::seconds UNCONDITIONAL_RELAY_DELAY = std::chrono::minutes{2};
-/** Headers download timeout expressed in microseconds
+static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
+/** Headers download timeout.
* Timeout = base + per_header * (expected number of headers) */
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
+static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
/** Protect at least this many outbound peers from disconnection due to slow/
* behind headers chain.
*/
@@ -93,8 +90,8 @@ static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seco
static const unsigned int MAX_GETDATA_SZ = 1000;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
-/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
-static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
+/** Time during which a peer must stall block download progress before being disconnected. */
+static constexpr auto BLOCK_STALLING_TIMEOUT = 2s;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
@@ -108,10 +105,10 @@ static const int MAX_BLOCKTXN_DEPTH = 10;
* degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
* want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
-/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
-static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
+/** Block download timeout base, expressed in multiples of the block interval (i.e. 10 min) */
+static constexpr double BLOCK_DOWNLOAD_TIMEOUT_BASE = 1;
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
-static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000;
+static constexpr double BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 0.5;
/** Maximum number of headers to announce when relaying blocks with headers message.*/
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
/** Maximum number of unconnecting headers announcements before DoS score */
@@ -119,17 +116,21 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
/** Average delay between local address broadcasts */
-static constexpr std::chrono::hours AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24};
+static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h;
/** Average delay between peer address broadcasts */
-static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
-/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and peers with noban permission bypass this, outbound peers get half this delay. */
-static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
+static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL = 30s;
+/** Average delay between trickled inventory transmissions for inbound peers.
+ * Blocks and peers with noban permission bypass this. */
+static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL = 5s;
+/** Average delay between trickled inventory transmissions for outbound peers.
+ * Use a smaller delay as there is less privacy concern for them.
+ * Blocks and peers with noban permission bypass this. */
+static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL = 2s;
/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
/** Maximum number of inventory items to send per transmission. */
-static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * count_seconds(INBOUND_INVENTORY_BROADCAST_INTERVAL);
/** The number of most recently announced transactions a peer can request. */
static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
@@ -138,9 +139,9 @@ static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
* peers, and random variations in the broadcast mechanism. */
static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
-static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
+static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL = 10min;
/** Maximum feefilter broadcast delay after significant change. */
-static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
+static constexpr auto MAX_FEEFILTER_CHANGE_DELAY = 5min;
/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
@@ -148,25 +149,6 @@ static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
/** the maximum percentage of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;
-struct COrphanTx {
- // When modifying, adapt the copy of this definition in tests/DoS_tests.
- CTransactionRef tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
- size_t list_pos;
-};
-
-/** Guards orphan transactions and extra txs for compact blocks */
-RecursiveMutex g_cs_orphans;
-/** Map from txid to orphan transaction record. Limited by
- * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
-std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-/** Index from wtxid into the mapOrphanTransactions to lookup orphan
- * transactions using their witness ids. */
-std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans);
-
-void EraseOrphansFor(NodeId peer);
-
// Internal stuff
namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */
@@ -463,7 +445,7 @@ private:
typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay GUARDED_BY(cs_main);
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
- std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
+ std::deque<std::pair<std::chrono::microseconds, MapRelay::iterator>> g_relay_expiration GUARDED_BY(cs_main);
/**
* When a peer sends us a valid block, instruct it to announce blocks to us
@@ -479,35 +461,41 @@ private:
/** Number of peers from which we're downloading blocks. */
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
-};
-} // namespace
+ /** Storage for orphan information */
+ TxOrphanage m_orphanage;
-namespace {
-
- /** Number of preferable block download peers. */
- int nPreferredDownload GUARDED_BY(cs_main) = 0;
-
- struct IteratorComparator
- {
- template<typename I>
- bool operator()(const I& a, const I& b) const
- {
- return &(*a) < &(*b);
- }
- };
-
- /** Index from the parents' COutPoint into the mapOrphanTransactions. Used
- * to remove orphan transactions from the mapOrphanTransactions */
- std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
- /** Orphan transactions in vector for quick random eviction */
- std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans);
+ void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
* these are kept in a ring buffer */
- static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
+ std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
/** Offset into vExtraTxnForCompact to insert the next tx */
- static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+
+ void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman);
+ bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
+ BlockFilterType filter_type, uint32_t start_height,
+ const uint256& stop_hash, uint32_t max_height_diff,
+ const CBlockIndex*& stop_index,
+ BlockFilterIndex*& filter_index);
+ void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman);
+ void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman);
+ void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman);
+};
+} // namespace
+
+namespace {
+ /** Number of preferable block download peers. */
+ int nPreferredDownload GUARDED_BY(cs_main) = 0;
} // namespace
namespace {
@@ -518,50 +506,48 @@ namespace {
* and we're no longer holding the node's locks.
*/
struct CNodeState {
- //! The peer's address
- const CService address;
//! The best known block we know this peer has announced.
- const CBlockIndex *pindexBestKnownBlock;
+ const CBlockIndex* pindexBestKnownBlock{nullptr};
//! The hash of the last unknown block this peer has announced.
- uint256 hashLastUnknownBlock;
+ uint256 hashLastUnknownBlock{};
//! The last full block we both have.
- const CBlockIndex *pindexLastCommonBlock;
+ const CBlockIndex* pindexLastCommonBlock{nullptr};
//! The best header we have sent our peer.
- const CBlockIndex *pindexBestHeaderSent;
+ const CBlockIndex* pindexBestHeaderSent{nullptr};
//! Length of current-streak of unconnecting headers announcements
- int nUnconnectingHeaders;
+ int nUnconnectingHeaders{0};
//! Whether we've started headers synchronization with this peer.
- bool fSyncStarted;
+ bool fSyncStarted{false};
//! When to potentially disconnect peer for stalling headers download
- int64_t nHeadersSyncTimeout;
+ std::chrono::microseconds m_headers_sync_timeout{0us};
//! Since when we're stalling block download progress (in microseconds), or 0.
- int64_t nStallingSince;
+ std::chrono::microseconds m_stalling_since{0us};
std::list<QueuedBlock> vBlocksInFlight;
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
- int64_t nDownloadingSince;
- int nBlocksInFlight;
- int nBlocksInFlightValidHeaders;
+ std::chrono::microseconds m_downloading_since{0us};
+ int nBlocksInFlight{0};
+ int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
- bool fPreferredDownload;
+ bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
- bool fPreferHeaders;
+ bool fPreferHeaders{false};
//! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
- bool fPreferHeaderAndIDs;
+ bool fPreferHeaderAndIDs{false};
/**
* Whether this peer will send us cmpctblocks if we request them.
* This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
* but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
*/
- bool fProvidesHeaderAndIDs;
+ bool fProvidesHeaderAndIDs{false};
//! Whether this peer can give us witnesses
- bool fHaveWitness;
+ bool fHaveWitness{false};
//! Whether this peer wants witnesses in cmpctblocks/blocktxns
- bool fWantsCmpctWitness;
+ bool fWantsCmpctWitness{false};
/**
* If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
* otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
*/
- bool fSupportsDesiredCmpctVersion;
+ bool fSupportsDesiredCmpctVersion{false};
/** State used to enforce CHAIN_SYNC_TIMEOUT and EXTRA_PEER_CHECK_INTERVAL logic.
*
@@ -598,13 +584,13 @@ struct CNodeState {
bool m_protect;
};
- ChainSyncTimeoutState m_chain_sync;
+ ChainSyncTimeoutState m_chain_sync{0, nullptr, false, false};
//! Time of last new block announcement
- int64_t m_last_block_announcement;
+ int64_t m_last_block_announcement{0};
//! Whether this peer is an inbound connection
- bool m_is_inbound;
+ const bool m_is_inbound;
//! A rolling bloom filter of all announced tx CInvs to this peer.
CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
@@ -612,29 +598,9 @@ struct CNodeState {
//! Whether this peer relays txs via wtxid
bool m_wtxid_relay{false};
- CNodeState(CAddress addrIn, bool is_inbound)
- : address(addrIn), m_is_inbound(is_inbound)
+ CNodeState(bool is_inbound)
+ : m_is_inbound(is_inbound)
{
- pindexBestKnownBlock = nullptr;
- hashLastUnknownBlock.SetNull();
- pindexLastCommonBlock = nullptr;
- pindexBestHeaderSent = nullptr;
- nUnconnectingHeaders = 0;
- fSyncStarted = false;
- nHeadersSyncTimeout = 0;
- nStallingSince = 0;
- nDownloadingSince = 0;
- nBlocksInFlight = 0;
- nBlocksInFlightValidHeaders = 0;
- fPreferredDownload = false;
- fPreferHeaders = false;
- fPreferHeaderAndIDs = false;
- fProvidesHeaderAndIDs = false;
- fHaveWitness = false;
- fWantsCmpctWitness = false;
- fSupportsDesiredCmpctVersion = false;
- m_chain_sync = { 0, nullptr, false, false };
- m_last_block_announcement = 0;
m_recently_announced_invs.reset();
}
};
@@ -672,11 +638,11 @@ bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
}
if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
// First block on the queue was received, update the start download time for the next one
- state->nDownloadingSince = std::max(state->nDownloadingSince, count_microseconds(GetTime<std::chrono::microseconds>()));
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
}
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
- state->nStallingSince = 0;
+ state->m_stalling_since = 0us;
mapBlocksInFlight.erase(itInFlight);
return true;
}
@@ -706,7 +672,7 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
- state->nDownloadingSince = GetTime<std::chrono::microseconds>().count();
+ state->m_downloading_since = GetTime<std::chrono::microseconds>();
}
if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
nPeersWithValidatedDownloads++;
@@ -717,41 +683,6 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
return true;
}
-/** Check whether the last unknown block a peer advertised is not yet known. */
-static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- CNodeState *state = State(nodeid);
- assert(state != nullptr);
-
- if (!state->hashLastUnknownBlock.IsNull()) {
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
- if (pindex && pindex->nChainWork > 0) {
- if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
- state->pindexBestKnownBlock = pindex;
- }
- state->hashLastUnknownBlock.SetNull();
- }
- }
-}
-
-/** Update tracking information about which blocks a peer is assumed to have. */
-static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- CNodeState *state = State(nodeid);
- assert(state != nullptr);
-
- ProcessBlockAvailability(nodeid);
-
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- if (pindex && pindex->nChainWork > 0) {
- // An actually better block was announced.
- if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
- state->pindexBestKnownBlock = pindex;
- }
- } else {
- // An unknown block was announced; just assume that the latest one is the best one.
- state->hashLastUnknownBlock = hash;
- }
-}
-
void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
{
AssertLockHeld(cs_main);
@@ -801,9 +732,9 @@ bool PeerManagerImpl::TipMayBeStale()
return m_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
}
-static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::CanDirectFetch(const Consensus::Params &consensusParams)
{
- return ::ChainActive().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+ return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
}
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -815,6 +746,41 @@ static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIV
return false;
}
+/** Check whether the last unknown block a peer advertised is not yet known. */
+void PeerManagerImpl::ProcessBlockAvailability(NodeId nodeid) {
+ CNodeState *state = State(nodeid);
+ assert(state != nullptr);
+
+ if (!state->hashLastUnknownBlock.IsNull()) {
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
+ if (pindex && pindex->nChainWork > 0) {
+ if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
+ state->pindexBestKnownBlock = pindex;
+ }
+ state->hashLastUnknownBlock.SetNull();
+ }
+ }
+}
+
+/** Update tracking information about which blocks a peer is assumed to have. */
+void PeerManagerImpl::UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+ CNodeState *state = State(nodeid);
+ assert(state != nullptr);
+
+ ProcessBlockAvailability(nodeid);
+
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
+ if (pindex && pindex->nChainWork > 0) {
+ // An actually better block was announced.
+ if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
+ state->pindexBestKnownBlock = pindex;
+ }
+ } else {
+ // An unknown block was announced; just assume that the latest one is the best one.
+ state->hashLastUnknownBlock = hash;
+ }
+}
+
void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller)
{
if (count == 0)
@@ -827,7 +793,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
- if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < ::ChainActive().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
@@ -835,7 +801,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
if (state->pindexLastCommonBlock == nullptr) {
// Bootstrap quickly by guessing a parent of our best tip is the forking point.
// Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = ::ChainActive()[std::min(state->pindexBestKnownBlock->nHeight, ::ChainActive().Height())];
+ state->pindexLastCommonBlock = m_chainman.ActiveChain()[std::min(state->pindexBestKnownBlock->nHeight, m_chainman.ActiveChain().Height())];
}
// If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
@@ -878,7 +844,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
// We wouldn't download this block or its descendants from this peer.
return;
}
- if (pindex->nStatus & BLOCK_HAVE_DATA || ::ChainActive().Contains(pindex)) {
+ if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
@@ -970,12 +936,10 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
void PeerManagerImpl::InitializeNode(CNode *pnode)
{
- CAddress addr = pnode->addr;
- std::string addrName = pnode->GetAddrName();
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn()));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(pnode->IsInboundConn()));
assert(m_txrequest.Count(nodeid) == 0);
}
{
@@ -1040,7 +1004,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTim
for (const QueuedBlock& entry : state->vBlocksInFlight) {
mapBlocksInFlight.erase(entry.hash);
}
- EraseOrphansFor(nodeid);
+ WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
@@ -1112,17 +1076,12 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats)
ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
}
- stats.m_ping_wait_usec = count_microseconds(ping_wait);
+ stats.m_ping_wait = ping_wait;
return true;
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapOrphanTransactions
-//
-
-static void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
@@ -1133,126 +1092,6 @@ static void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_L
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn;
}
-bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
-{
- const uint256& hash = tx->GetHash();
- if (mapOrphanTransactions.count(hash))
- return false;
-
- // Ignore big transactions, to avoid a
- // send-big-orphans memory exhaustion attack. If a peer has a legitimate
- // large transaction with a missing parent then we assume
- // it will rebroadcast it later, after the parent transaction(s)
- // have been mined or received.
- // 100 orphans, each of which is at most 100,000 bytes big is
- // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
- unsigned int sz = GetTransactionWeight(*tx);
- if (sz > MAX_STANDARD_TX_WEIGHT)
- {
- LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
- return false;
- }
-
- auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, g_orphan_list.size()});
- assert(ret.second);
- g_orphan_list.push_back(ret.first);
- // Allow for lookups in the orphan pool by wtxid, as well as txid
- g_orphans_by_wtxid.emplace(tx->GetWitnessHash(), ret.first);
- for (const CTxIn& txin : tx->vin) {
- mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
- }
-
- AddToCompactExtraTransactions(tx);
-
- LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
- mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
- return true;
-}
-
-int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
-{
- std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
- if (it == mapOrphanTransactions.end())
- return 0;
- for (const CTxIn& txin : it->second.tx->vin)
- {
- auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itPrev == mapOrphanTransactionsByPrev.end())
- continue;
- itPrev->second.erase(it);
- if (itPrev->second.empty())
- mapOrphanTransactionsByPrev.erase(itPrev);
- }
-
- size_t old_pos = it->second.list_pos;
- assert(g_orphan_list[old_pos] == it);
- if (old_pos + 1 != g_orphan_list.size()) {
- // Unless we're deleting the last entry in g_orphan_list, move the last
- // entry to the position we're deleting.
- auto it_last = g_orphan_list.back();
- g_orphan_list[old_pos] = it_last;
- it_last->second.list_pos = old_pos;
- }
- g_orphan_list.pop_back();
- g_orphans_by_wtxid.erase(it->second.tx->GetWitnessHash());
-
- mapOrphanTransactions.erase(it);
- return 1;
-}
-
-void EraseOrphansFor(NodeId peer)
-{
- LOCK(g_cs_orphans);
- int nErased = 0;
- std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- std::map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- nErased += EraseOrphanTx(maybeErase->second.tx->GetHash());
- }
- }
- if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
-}
-
-
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
-{
- LOCK(g_cs_orphans);
-
- unsigned int nEvicted = 0;
- static int64_t nNextSweep;
- int64_t nNow = GetTime();
- if (nNextSweep <= nNow) {
- // Sweep out expired orphan pool entries:
- int nErased = 0;
- int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
- std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- std::map<uint256, COrphanTx>::iterator maybeErase = iter++;
- if (maybeErase->second.nTimeExpire <= nNow) {
- nErased += EraseOrphanTx(maybeErase->second.tx->GetHash());
- } else {
- nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
- }
- }
- // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
- nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
- if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
- }
- FastRandomContext rng;
- while (mapOrphanTransactions.size() > nMaxOrphans)
- {
- // Evict a random orphan:
- size_t randompos = rng.randrange(g_orphan_list.size());
- EraseOrphanTx(g_orphan_list[randompos]->first);
- ++nEvicted;
- }
- return nEvicted;
-}
-
void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
{
assert(howmuch > 0);
@@ -1358,10 +1197,10 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
// active chain if they are no more than a month older (both in time, and in
// best equivalent proof of work) than the best header chain we know about and
// we fully-validated them at some point.
-static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
AssertLockHeld(cs_main);
- if (::ChainActive().Contains(pindex)) return true;
+ if (m_chainman.ActiveChain().Contains(pindex)) return true;
return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
@@ -1385,6 +1224,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
+ assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -1412,43 +1252,15 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
}
/**
- * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
+ * Evict orphan txn pool entries based on a newly connected
* block, remember the recently confirmed transactions, and delete tracked
* announcements for them. Also save the time of the last tip update.
*/
void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
- {
- LOCK(g_cs_orphans);
-
- std::vector<uint256> vOrphanErase;
+ m_orphanage.EraseForBlock(*pblock);
+ m_last_tip_update = GetTime();
- for (const CTransactionRef& ptx : pblock->vtx) {
- const CTransaction& tx = *ptx;
-
- // Which orphan pool entries must we evict?
- for (const auto& txin : tx.vin) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = *(*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
- }
- }
- }
-
- // Erase orphan transactions included or precluded by this block
- if (vOrphanErase.size()) {
- int nErased = 0;
- for (const uint256& orphanHash : vOrphanErase) {
- nErased += EraseOrphanTx(orphanHash);
- }
- LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
- }
-
- m_last_tip_update = GetTime();
- }
{
LOCK(m_recent_confirmed_transactions_mutex);
for (const auto& ptx : pblock->vtx) {
@@ -1538,7 +1350,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
/**
* Update our best height and announce any block hashes which weren't previously
- * in ::ChainActive() to our peers.
+ * in m_chainman.ActiveChain() to our peers.
*/
void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
{
@@ -1600,7 +1412,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
// the tip yet so we have no way to check this directly here. Instead we
// just check that there are currently no other blocks in flight.
else if (state.IsValid() &&
- !::ChainstateActive().IsInitialBlockDownload() &&
+ !m_chainman.ActiveChainstate().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first);
@@ -1619,25 +1431,18 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid)
{
assert(recentRejects);
- if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
+ if (m_chainman.ActiveChain().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
// If the chain tip has changed previously rejected transactions
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
// or a double-spend. Reset the rejects filter and give those
// txs a second chance.
- hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
+ hashRecentRejectsChainTip = m_chainman.ActiveChain().Tip()->GetBlockHash();
recentRejects->reset();
}
const uint256& hash = gtxid.GetHash();
- {
- LOCK(g_cs_orphans);
- if (!gtxid.IsWtxid() && mapOrphanTransactions.count(hash)) {
- return true;
- } else if (gtxid.IsWtxid() && g_orphans_by_wtxid.count(hash)) {
- return true;
- }
- }
+ if (m_orphanage.HaveTx(gtxid)) return true;
{
LOCK(m_recent_confirmed_transactions_mutex);
@@ -1647,9 +1452,9 @@ bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid)
return recentRejects->contains(hash) || m_mempool.exists(gtxid);
}
-bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::AlreadyHaveBlock(const uint256& block_hash)
{
- return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;
+ return m_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;
}
void PeerManagerImpl::SendPings()
@@ -1728,7 +1533,7 @@ static void RelayAddress(const CNode& originator,
connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman)
+void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman)
{
bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
@@ -1745,7 +1550,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
bool need_activate_chain = false;
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
if (pindex) {
if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
pindex->IsValid(BLOCK_VALID_TREE)) {
@@ -1760,13 +1565,13 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
if (pindex) {
send = BlockRequestAllowed(pindex, consensusParams);
if (!send) {
@@ -1788,7 +1593,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
if (send && !pfrom.HasPermission(PF_NOBAN) && (
- (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId());
@@ -1855,7 +1660,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
// instead we respond with the full, non-compact block.
bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
+ if (CanDirectFetch(consensusParams) && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
@@ -1876,7 +1681,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
// and we want it right after the last block so they don't
// wait for other stuff first.
std::vector<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
+ vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()));
connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
peer.m_continuation_block.SetNull();
}
@@ -2055,9 +1860,9 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// don't connect before giving DoS points
// - Once a headers message is received that is valid and does connect,
// nUnconnectingHeaders gets reset back to 0.
- if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -2085,7 +1890,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// If we don't have the last header, then they'll have given us
// something new (if these headers are valid).
- if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
received_new_header = true;
}
}
@@ -2113,27 +1918,27 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
- if (received_new_header && pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) {
+ if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
+ // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
- if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) {
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) {
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = pindexLast;
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
@@ -2146,7 +1951,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// very large reorg at a time we think we're close to caught up to
// the main chain -- this shouldn't really happen. Bail out on the
// direct fetch and rely on parallel download instead.
- if (!::ChainActive().Contains(pindexWalk)) {
+ if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
pindexLast->GetBlockHash().ToString(),
pindexLast->nHeight);
@@ -2179,7 +1984,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
// If we're in IBD, we want outbound peers that will serve us a useful
// chain. Disconnect peers that are on chains with insufficient work.
- if (::ChainstateActive().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
@@ -2187,7 +1992,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// us sync -- disconnect if it is an outbound disconnection
// candidate.
// Note: We compare their tip to nMinimumChainWork (rather than
- // ::ChainActive().Tip()) because we won't start block download
+ // m_chainman.ActiveChain().Tip()) because we won't start block download
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
@@ -2204,7 +2009,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// thus always subject to eviction under the bad/lagging chain logic.
// See ChainSyncTimeoutState.
if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
- if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
++m_outbound_peers_with_protect_from_disconnect;
@@ -2232,25 +2037,17 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
const uint256 orphanHash = *orphan_work_set.begin();
orphan_work_set.erase(orphan_work_set.begin());
- auto orphan_it = mapOrphanTransactions.find(orphanHash);
- if (orphan_it == mapOrphanTransactions.end()) continue;
+ const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
+ if (porphanTx == nullptr) continue;
- const CTransactionRef porphanTx = orphan_it->second.tx;
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), m_mempool, porphanTx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */);
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
- for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
- auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
- if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
- for (const auto& elem : it_by_prev->second) {
- orphan_work_set.insert(elem->first);
- }
- }
- }
- EraseOrphanTx(orphanHash);
+ m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set);
+ m_orphanage.EraseTx(orphanHash);
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
AddToCompactExtraTransactions(removedTx);
}
@@ -2259,10 +2056,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
orphanHash.ToString(),
- orphan_it->second.fromPeer,
+ from_peer,
state.ToString());
// Maybe punish peer that gave us an invalid orphan tx
- MaybePunishNodeForTx(orphan_it->second.fromPeer, state);
+ MaybePunishNodeForTx(from_peer, state);
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
@@ -2297,11 +2094,11 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
recentRejects->insert(porphanTx->GetHash());
}
}
- EraseOrphanTx(orphanHash);
+ m_orphanage.EraseTx(orphanHash);
break;
}
}
- m_mempool.check(&::ChainstateActive().CoinsTip());
+ m_mempool.check(m_chainman.ActiveChainstate());
}
/**
@@ -2319,7 +2116,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
+bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
BlockFilterType filter_type, uint32_t start_height,
const uint256& stop_hash, uint32_t max_height_diff,
const CBlockIndex*& stop_index,
@@ -2337,7 +2134,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par
{
LOCK(cs_main);
- stop_index = g_chainman.m_blockman.LookupBlockIndex(stop_hash);
+ stop_index = m_chainman.m_blockman.LookupBlockIndex(stop_hash);
// Check that the stop block exists and the peer would be allowed to fetch it.
if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
@@ -2382,7 +2179,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2424,7 +2221,7 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2479,7 +2276,7 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
+void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2662,7 +2459,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
//
// We skip this for block-relay-only peers to avoid potentially leaking
// information about our block-relay-only connections via address relay.
- if (fListen && !::ChainstateActive().IsInitialBlockDownload())
+ if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
FastRandomContext insecure_rand;
@@ -2979,7 +2776,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (best_block != nullptr) {
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
@@ -3035,7 +2832,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
a_recent_block = most_recent_block;
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, m_chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -3043,14 +2840,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
// Find the last block the caller has in the main chain
- const CBlockIndex* pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
+ const CBlockIndex* pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator);
// Send the rest of the chain
if (pindex)
- pindex = ::ChainActive().Next(pindex);
+ pindex = m_chainman.ActiveChain().Next(pindex);
int nLimit = 500;
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
- for (; pindex; pindex = ::ChainActive().Next(pindex))
+ for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
@@ -3060,7 +2857,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
@@ -3096,13 +2893,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(req.blockhash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
return;
}
- if (pindex->nHeight >= ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
+ if (pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_BLOCKTXN_DEPTH) {
CBlock block;
bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
assert(ret);
@@ -3140,7 +2937,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
return;
}
@@ -3150,7 +2947,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (locator.IsNull())
{
// If locator is null, return the hashStop block
- pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop);
+ pindex = m_chainman.m_blockman.LookupBlockIndex(hashStop);
if (!pindex) {
return;
}
@@ -3163,23 +2960,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
else
{
// Find the last block the caller has in the main chain
- pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
+ pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator);
if (pindex)
- pindex = ::ChainActive().Next(pindex);
+ pindex = m_chainman.ActiveChain().Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
- for (; pindex; pindex = ::ChainActive().Next(pindex))
+ for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
- // pindex can be nullptr either if we sent ::ChainActive().Tip() OR
- // if our peer has ::ChainActive().Tip() (and thus we are sending an empty
+ // pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR
+ // if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
// pindexBestHeaderSent to be our tip.
//
@@ -3190,7 +2987,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// without the new block. By resetting the BestHeaderSent, we ensure we
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
- nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
+ nodestate->pindexBestHeaderSent = pindex ? pindex : m_chainman.ActiveChain().Tip();
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return;
}
@@ -3258,24 +3055,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), m_mempool, ptx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
- m_mempool.check(&::ChainstateActive().CoinsTip());
+ 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());
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
- if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
- for (const auto& elem : it_by_prev->second) {
- peer->m_orphan_work_set.insert(elem->first);
- }
- }
- }
+ m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
pfrom.nLastTXTime = GetTime();
@@ -3324,17 +3114,20 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.AddKnownTx(parent_txid);
if (!AlreadyHaveTx(gtxid)) AddTxAnnouncement(pfrom, gtxid, current_time);
}
- AddOrphanTx(ptx, pfrom.GetId());
+
+ if (m_orphanage.AddTx(ptx, pfrom.GetId())) {
+ AddToCompactExtraTransactions(ptx);
+ }
// Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
m_txrequest.ForgetTxHash(tx.GetHash());
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
+ // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
- unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
+ unsigned int nEvicted = m_orphanage.LimitOrphans(nMaxOrphanTx);
if (nEvicted > 0) {
- LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted);
+ LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
}
} else {
LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
@@ -3427,14 +3220,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(cs_main);
- if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!::ChainstateActive().IsInitialBlockDownload())
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ if (!m_chainman.ActiveChainstate().IsInitialBlockDownload())
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
return;
}
- if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
received_new_header = true;
}
}
@@ -3474,7 +3267,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
- if (received_new_header && pindex->nChainWork > ::ChainActive().Tip()->nChainWork) {
+ if (received_new_header && pindex->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
@@ -3484,7 +3277,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
return;
- if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
+ if (pindex->nChainWork <= m_chainman.ActiveChain().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
if (fAlreadyInFlight) {
// We requested this block for some reason, but our mempool will probably be useless
@@ -3508,7 +3301,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
- if (pindex->nHeight <= ::ChainActive().Height() + 2) {
+ if (pindex->nHeight <= m_chainman.ActiveChain().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
@@ -4156,7 +3949,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// their chain has more work than ours, we should sync to it,
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
- if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork) {
+ if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {
if (state.m_chain_sync.m_timeout != 0) {
state.m_chain_sync.m_timeout = 0;
state.m_chain_sync.m_work_header = nullptr;
@@ -4168,7 +3961,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// where we checked against our tip.
// Either way, set a new timeout based on current tip.
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
- state.m_chain_sync.m_work_header = ::ChainActive().Tip();
+ state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();
state.m_chain_sync.m_sent_getheaders = false;
} else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
// No evidence yet that our peer has synced to a chain with work equal to that
@@ -4181,7 +3974,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
} else {
assert(state.m_chain_sync.m_work_header);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -4418,7 +4211,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
auto current_time = GetTime<std::chrono::microseconds>();
if (fListen && pto->RelayAddrsWithConn() &&
- !::ChainstateActive().IsInitialBlockDownload() &&
+ !m_chainman.ActiveChainstate().IsInitialBlockDownload() &&
pto->m_next_local_addr_send < current_time) {
// If we've sent before, clear the bloom filter for the peer, so that our
// self-announcement will actually go out.
@@ -4479,13 +4272,19 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
- pindexBestHeader = ::ChainActive().Tip();
+ pindexBestHeader = m_chainman.ActiveChain().Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
- state.nHeadersSyncTimeout = count_microseconds(current_time) + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
+ state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
+ (
+ // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
+ // to maintain precision
+ std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
+ (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / consensusParams.nPowTargetSpacing
+ );
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
/* If possible, start at the block preceding the currently
@@ -4498,7 +4297,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexStart), uint256()));
}
}
@@ -4525,11 +4324,11 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
bool fFoundStartingHeader = false;
// Try to find first header that our peer doesn't have, and
// then send all headers past that one. If we come across any
- // headers that aren't on ::ChainActive(), give up.
+ // headers that aren't on m_chainman.ActiveChain(), give up.
for (const uint256& hash : peer->m_blocks_for_headers_relay) {
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
assert(pindex);
- if (::ChainActive()[pindex->nHeight] != pindex) {
+ if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) {
// Bail out if we reorged away from this block
fRevertToInv = true;
break;
@@ -4619,15 +4418,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// in the past.
if (!peer->m_blocks_for_headers_relay.empty()) {
const uint256& hashToAnnounce = peer->m_blocks_for_headers_relay.back();
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);
assert(pindex);
// Warn if we're announcing a block that is not on the main chain.
// This should be very rare and could be optimized out.
// Just log for now.
- if (::ChainActive()[pindex->nHeight] != pindex) {
+ if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) {
LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), ::ChainActive().Tip()->GetBlockHash().ToString());
+ hashToAnnounce.ToString(), m_chainman.ActiveChain().Tip()->GetBlockHash().ToString());
}
// If the peer's chain has this block, don't inv it back.
@@ -4666,10 +4465,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->IsInboundConn()) {
- pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{m_connman.PoissonNextSendInbound(count_microseconds(current_time), INVENTORY_BROADCAST_INTERVAL)};
+ pto->m_tx_relay->nNextInvSend = m_connman.PoissonNextSendInbound(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
} else {
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
+ pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
}
}
@@ -4757,20 +4555,20 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
nRelayedTransactions++;
{
// Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < count_microseconds(current_time))
+ while (!g_relay_expiration.empty() && g_relay_expiration.front().first < current_time)
{
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
+ mapRelay.erase(g_relay_expiration.front().second);
+ g_relay_expiration.pop_front();
}
auto ret = mapRelay.emplace(txid, std::move(txinfo.tx));
if (ret.second) {
- vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret.first);
+ g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret.first);
}
// Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
auto ret2 = mapRelay.emplace(wtxid, ret.first->second);
if (ret2.second) {
- vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret2.first);
+ g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret2.first);
}
}
if (vInv.size() == MAX_INV_SZ) {
@@ -4795,7 +4593,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Detect whether we're stalling
current_time = GetTime<std::chrono::microseconds>();
- if (state.nStallingSince && state.nStallingSince < count_microseconds(current_time) - 1000000 * BLOCK_STALLING_TIMEOUT) {
+ if (state.m_stalling_since.count() && state.m_stalling_since < current_time - BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
// should only happen during initial block download.
@@ -4803,7 +4601,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
pto->fDisconnect = true;
return true;
}
- // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
+ // In case there is a block that has been in flight from this peer for block_interval * (1 + 0.5 * N)
// (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
// We compensate for other peers to prevent killing off peers due to our own downstream link
// being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
@@ -4811,17 +4609,17 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
- if (count_microseconds(current_time) > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
+ if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
}
// Check for headers sync timeouts
- if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
+ if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) {
// Detect whether this is a stalling initial-headers-sync peer
if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
- if (count_microseconds(current_time) > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
+ if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without the noban permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
@@ -4840,13 +4638,13 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// this peer (eventually).
state.fSyncStarted = false;
nSyncStarted--;
- state.nHeadersSyncTimeout = 0;
+ state.m_headers_sync_timeout = 0us;
}
}
} else {
// After we've caught up once, reset the timeout so we can't trigger
// disconnect later.
- state.nHeadersSyncTimeout = std::numeric_limits<int64_t>::max();
+ state.m_headers_sync_timeout = std::chrono::microseconds::max();
}
}
@@ -4858,7 +4656,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
@@ -4870,8 +4668,8 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
pindex->nHeight, pto->GetId());
}
if (state.nBlocksInFlight == 0 && staller != -1) {
- if (State(staller)->nStallingSince == 0) {
- State(staller)->nStallingSince = count_microseconds(current_time);
+ if (State(staller)->m_stalling_since == 0us) {
+ State(staller)->m_stalling_since = current_time;
LogPrint(BCLog::NET, "Stall started peer=%d\n", staller);
}
}
@@ -4924,10 +4722,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
- pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) - 1;
+ pto->m_tx_relay->m_next_send_feefilter = 0us;
}
}
- if (count_microseconds(current_time) > pto->m_tx_relay->nextSendTimeFeeFilter) {
+ if (current_time > pto->m_tx_relay->m_next_send_feefilter) {
CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
@@ -4935,28 +4733,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->m_tx_relay->lastSentFeeFilter = filterToSend;
}
- pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(count_microseconds(current_time), AVG_FEEFILTER_BROADCAST_INTERVAL);
+ pto->m_tx_relay->m_next_send_feefilter = PoissonNextSend(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
}
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
- else if (count_microseconds(current_time) + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
+ else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < pto->m_tx_relay->m_next_send_feefilter &&
(currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
- pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ pto->m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
}
}
} // release cs_main
return true;
}
-
-class CNetProcessingCleanup
-{
-public:
- CNetProcessingCleanup() {}
- ~CNetProcessingCleanup() {
- // orphan transactions
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
- g_orphans_by_wtxid.clear();
- }
-};
-static CNetProcessingCleanup instance_of_cnetprocessingcleanup;
diff --git a/src/net_processing.h b/src/net_processing.h
index d7be453df5..6f900410a7 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -15,7 +15,6 @@ class CTxMemPool;
class ChainstateManager;
extern RecursiveMutex cs_main;
-extern RecursiveMutex g_cs_orphans;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
@@ -30,7 +29,7 @@ struct CNodeStateStats {
int nSyncHeight = -1;
int nCommonHeight = -1;
int m_starting_height = -1;
- int64_t m_ping_wait_usec;
+ std::chrono::microseconds m_ping_wait;
std::vector<int> vHeightInFlight;
};
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 85e46fd373..69edc15c66 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -221,25 +221,34 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS
}; // namespace torv3
-/**
- * Parse a TOR address and set this object to it.
- *
- * @returns Whether or not the operation was successful.
- *
- * @see CNetAddr::IsTor()
- */
-bool CNetAddr::SetSpecial(const std::string& str)
+bool CNetAddr::SetSpecial(const std::string& addr)
+{
+ if (!ValidAsCString(addr)) {
+ return false;
+ }
+
+ if (SetTor(addr)) {
+ return true;
+ }
+
+ if (SetI2P(addr)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool CNetAddr::SetTor(const std::string& addr)
{
static const char* suffix{".onion"};
static constexpr size_t suffix_len{6};
- if (!ValidAsCString(str) || str.size() <= suffix_len ||
- str.substr(str.size() - suffix_len) != suffix) {
+ if (addr.size() <= suffix_len || addr.substr(addr.size() - suffix_len) != suffix) {
return false;
}
bool invalid;
- const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
+ const auto& input = DecodeBase32(addr.substr(0, addr.size() - suffix_len).c_str(), &invalid);
if (invalid) {
return false;
@@ -275,6 +284,34 @@ bool CNetAddr::SetSpecial(const std::string& str)
return false;
}
+bool CNetAddr::SetI2P(const std::string& addr)
+{
+ // I2P addresses that we support consist of 52 base32 characters + ".b32.i2p".
+ static constexpr size_t b32_len{52};
+ static const char* suffix{".b32.i2p"};
+ static constexpr size_t suffix_len{8};
+
+ if (addr.size() != b32_len + suffix_len || ToLower(addr.substr(b32_len)) != suffix) {
+ return false;
+ }
+
+ // Remove the ".b32.i2p" suffix and pad to a multiple of 8 chars, so DecodeBase32()
+ // can decode it.
+ const std::string b32_padded = addr.substr(0, b32_len) + "====";
+
+ bool invalid;
+ const auto& address_bytes = DecodeBase32(b32_padded.c_str(), &invalid);
+
+ if (invalid || address_bytes.size() != ADDR_I2P_SIZE) {
+ return false;
+ }
+
+ m_net = NET_I2P;
+ m_addr.assign(address_bytes.begin(), address_bytes.end());
+
+ return true;
+}
+
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
m_net = NET_IPV4;
@@ -841,6 +878,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well
case NET_ONION: return REACH_PRIVATE;
}
+ case NET_I2P:
+ switch (ourNet) {
+ case NET_I2P: return REACH_PRIVATE;
+ default: return REACH_DEFAULT;
+ }
case NET_TEREDO:
switch(ourNet) {
default: return REACH_DEFAULT;
diff --git a/src/netaddress.h b/src/netaddress.h
index d0986557f7..897ce46cda 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -151,7 +151,16 @@ class CNetAddr
bool SetInternal(const std::string& name);
- bool SetSpecial(const std::string &strName); // for Tor addresses
+ /**
+ * Parse a Tor or I2P address and set this object to it.
+ * @param[in] addr Address to parse, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
+ */
+ bool SetSpecial(const std::string& addr);
+
bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@@ -249,6 +258,25 @@ class CNetAddr
private:
/**
+ * Parse a Tor address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * 6hzph5hv6337r6p2.onion.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor()
+ */
+ bool SetTor(const std::string& addr);
+
+ /**
+ * Parse an I2P address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsI2P()
+ */
+ bool SetI2P(const std::string& addr);
+
+ /**
* BIP155 network ids recognized by this software.
*/
enum BIP155Network : uint8_t {
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 0c5b3a220e..88c36ed86c 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -5,6 +5,7 @@
#include <netbase.h>
+#include <compat.h>
#include <sync.h>
#include <tinyformat.h>
#include <util/sock.h>
@@ -14,6 +15,7 @@
#include <util/time.h>
#include <atomic>
+#include <chrono>
#include <cstdint>
#include <functional>
#include <limits>
@@ -29,10 +31,6 @@
#include <poll.h>
#endif
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif
-
// Settings
static Mutex g_proxyinfo_mutex;
static proxyType proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
@@ -41,7 +39,7 @@ int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
-static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
+int g_socks5_recv_timeout = 20 * 1000;
static std::atomic<bool> interruptSocks5Recv(false);
enum Network ParseNetwork(const std::string& net_in) {
@@ -53,6 +51,9 @@ enum Network ParseNetwork(const std::string& net_in) {
LogPrintf("Warning: net name 'tor' is deprecated and will be removed in the future. You should use 'onion' instead.\n");
return NET_ONION;
}
+ if (net == "i2p") {
+ return NET_I2P;
+ }
return NET_UNROUTABLE;
}
@@ -77,7 +78,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_I2P || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
names.emplace_back(GetNetworkName(network));
}
if (append_unroutable) {
@@ -360,9 +361,6 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
- // Maximum time to wait for I/O readiness. It will take up until this time
- // (in millis) to break off in case of an interruption.
- const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
@@ -373,10 +371,11 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
} else { // Other error or blocking
int nErr = WSAGetLastError();
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
- // Only wait at most maxWait milliseconds at a time, unless
+ // Only wait at most MAX_WAIT_FOR_IO at a time, unless
// we're approaching the end of the specified total timeout
- int timeout_ms = std::min(endTime - curTime, maxWait);
- if (!sock.Wait(std::chrono::milliseconds{timeout_ms}, Sock::RECV)) {
+ const auto remaining = std::chrono::milliseconds{endTime - curTime};
+ const auto timeout = std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ if (!sock.Wait(timeout, Sock::RECV)) {
return IntrRecvError::NetworkError;
}
} else {
@@ -390,13 +389,6 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}
-/** Credentials for proxy authentication */
-struct ProxyCredentials
-{
- std::string username;
- std::string password;
-};
-
/** Convert SOCKS5 reply to an error message */
static std::string Socks5ErrorString(uint8_t err)
{
@@ -440,7 +432,7 @@ static std::string Socks5ErrorString(uint8_t err)
* @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
* Version 5</a>
*/
-static bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock)
+bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock)
{
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
@@ -463,7 +455,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials*
return error("Error sending to proxy");
}
uint8_t pchRet1[2];
- if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet1, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
@@ -486,7 +478,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials*
}
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
uint8_t pchRetA[2];
- if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRetA, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
return error("Error reading proxy authentication response");
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
@@ -511,7 +503,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials*
return error("Error sending to proxy");
}
uint8_t pchRet2[4];
- if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet2, 4, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
if (recvr == IntrRecvError::Timeout) {
/* If a timeout happens here, this effectively means we timed out while connecting
* to the remote node. This is very common for Tor, so do not print an
@@ -535,16 +527,16 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials*
uint8_t pchRet3[256];
switch (pchRet2[3])
{
- case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, sock); break;
- case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, sock); break;
+ case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, g_socks5_recv_timeout, sock); break;
+ case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, g_socks5_recv_timeout, sock); break;
case SOCKS5Atyp::DOMAINNAME:
{
- recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, sock);
+ recvr = InterruptibleRecv(pchRet3, 1, g_socks5_recv_timeout, sock);
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
- recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, sock);
+ recvr = InterruptibleRecv(pchRet3, nRecv, g_socks5_recv_timeout, sock);
break;
}
default: return error("Error: malformed proxy response");
@@ -552,7 +544,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials*
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
- if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet3, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
return error("Error reading from proxy");
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
diff --git a/src/netbase.h b/src/netbase.h
index 847a72ca8e..751f7eb3f0 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -18,6 +18,7 @@
#include <memory>
#include <stdint.h>
#include <string>
+#include <type_traits>
#include <vector>
extern int nConnectTimeout;
@@ -28,6 +29,22 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000;
//! -dns default
static const int DEFAULT_NAME_LOOKUP = true;
+enum class ConnectionDirection {
+ None = 0,
+ In = (1U << 0),
+ Out = (1U << 1),
+ Both = (In | Out),
+};
+static inline ConnectionDirection& operator|=(ConnectionDirection& a, ConnectionDirection b) {
+ using underlying = typename std::underlying_type<ConnectionDirection>::type;
+ a = ConnectionDirection(underlying(a) | underlying(b));
+ return a;
+}
+static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
+ using underlying = typename std::underlying_type<ConnectionDirection>::type;
+ return (underlying(a) & underlying(b));
+}
+
class proxyType
{
public:
@@ -40,6 +57,13 @@ public:
bool randomize_credentials;
};
+/** Credentials for proxy authentication */
+struct ProxyCredentials
+{
+ std::string username;
+ std::string password;
+};
+
enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
/** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */
@@ -77,4 +101,6 @@ bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
bool SetSocketNoDelay(const SOCKET& hSocket);
void InterruptSocks5(bool interrupt);
+bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& socket);
+
#endif // BITCOIN_NETBASE_H
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index f4f86cdbe9..263dcff657 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -12,7 +12,8 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
{
assert(node.mempool);
LOCK2(cs_main, node.mempool->cs);
- CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
+ assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
+ CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
if (!mempool_view.GetCoin(coin.first, coin.second)) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 06fcc33725..88bdba5953 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -83,7 +83,7 @@ static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, con
//! Calculate statistics about the unspent transaction output set
template <typename T>
-static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
+static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
@@ -92,7 +92,8 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const
stats.hashBlock = pcursor->GetBestBlock();
{
LOCK(cs_main);
- stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
+ assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
+ stats.nHeight = blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
}
PrepareHash(hash_obj, stats);
@@ -126,19 +127,19 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const
return true;
}
-bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
{
switch (hash_type) {
case(CoinStatsHashType::HASH_SERIALIZED): {
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- return GetUTXOStats(view, stats, ss, interruption_point);
+ return GetUTXOStats(view, blockman, stats, ss, interruption_point);
}
case(CoinStatsHashType::MUHASH): {
MuHash3072 muhash;
- return GetUTXOStats(view, stats, muhash, interruption_point);
+ return GetUTXOStats(view, blockman, stats, muhash, interruption_point);
}
case(CoinStatsHashType::NONE): {
- return GetUTXOStats(view, stats, nullptr, interruption_point);
+ return GetUTXOStats(view, blockman, stats, nullptr, interruption_point);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index f02b95235f..83f228aa7e 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <uint256.h>
+#include <validation.h>
#include <cstdint>
#include <functional>
@@ -36,6 +37,6 @@ struct CCoinsStats
};
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index ec976fe9bf..9a6cee6433 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -96,7 +96,7 @@ public:
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
- size_t getNodeCount(CConnman::NumConnections flags) override
+ size_t getNodeCount(ConnectionDirection flags) override
{
return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0;
}
@@ -182,18 +182,21 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- return ::ChainActive().Height();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ return m_context->chainman->ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return m_context->chainman->ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- if (::ChainActive().Tip()) {
- return ::ChainActive().Tip()->GetBlockTime();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ if (m_context->chainman->ActiveChain().Tip()) {
+ return m_context->chainman->ActiveChain().Tip()->GetBlockTime();
}
return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
@@ -202,11 +205,15 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- tip = ::ChainActive().Tip();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ tip = m_context->chainman->ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
- bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate()));
+ return m_context->chainman->ActiveChainstate().IsInitialBlockDownload();
+ }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override
@@ -231,7 +238,8 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate()));
+ return m_context->chainman->ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
{
@@ -441,13 +449,15 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- return CheckFinalTx(::ChainActive().Tip(), tx);
+ assert(std::addressof(::ChainActive()) == std::addressof(m_node.chainman->ActiveChain()));
+ return CheckFinalTx(m_node.chainman->ActiveChain().Tip(), tx);
}
Optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(active, locator)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
return nullopt;
@@ -456,7 +466,8 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- return FillBlock(g_chainman.m_blockman.LookupBlockIndex(hash), block, lock, active);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
{
@@ -468,7 +479,8 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- if (const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
}
@@ -479,8 +491,10 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash);
- const CBlockIndex* ancestor = g_chainman.m_blockman.LookupBlockIndex(ancestor_hash);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
}
@@ -488,8 +502,10 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- const CBlockIndex* block1 = g_chainman.m_blockman.LookupBlockIndex(block_hash1);
- const CBlockIndex* block2 = g_chainman.m_blockman.LookupBlockIndex(block_hash2);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
// output uninitialized.
@@ -499,7 +515,8 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash));
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ return GuessVerificationProgress(Params().TxData(), m_node.chainman->m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override
{
@@ -511,7 +528,8 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- if (CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
// Check pprev to not segfault if min_height is too low
@@ -526,6 +544,12 @@ public:
LOCK(m_node.mempool->cs);
return IsRBFOptIn(tx, *m_node.mempool);
}
+ bool isInMempool(const uint256& txid) override
+ {
+ if (!m_node.mempool) return false;
+ LOCK(m_node.mempool->cs);
+ return m_node.mempool->exists(txid);
+ }
bool hasDescendantsInMempool(const uint256& txid) override
{
if (!m_node.mempool) return false;
@@ -595,7 +619,10 @@ public:
return ::fHavePruned;
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
- bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_node.chainman->ActiveChainstate()));
+ return m_node.chainman->ActiveChainstate().IsInitialBlockDownload();
+ }
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 3b3fab7b6b..97763f4fa2 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -39,9 +39,10 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
LOCK(cs_main);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
- CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
+ CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
// IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
@@ -53,7 +54,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(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */,
+ const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
true /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
@@ -62,7 +63,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
}
// Try to submit the transaction to the mempool.
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */,
+ const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
false /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index bb444f22b3..ee2462ed74 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -198,42 +198,38 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
- if(role == Qt::DisplayRole || role == Qt::EditRole)
- {
- switch(index.column())
- {
+ const auto column = static_cast<ColumnIndex>(index.column());
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ switch (column) {
case Label:
- if(rec->label.isEmpty() && role == Qt::DisplayRole)
- {
+ if (rec->label.isEmpty() && role == Qt::DisplayRole) {
return tr("(no label)");
- }
- else
- {
+ } else {
return rec->label;
}
case Address:
return rec->address;
- }
- }
- else if (role == Qt::FontRole)
- {
- QFont font;
- if(index.column() == Address)
- {
- font = GUIUtil::fixedPitchFont();
- }
- return font;
- }
- else if (role == TypeRole)
- {
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ } else if (role == Qt::FontRole) {
+ switch (column) {
+ case Label:
+ return QFont();
+ case Address:
+ return GUIUtil::fixedPitchFont();
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ } else if (role == TypeRole) {
switch(rec->type)
{
case AddressTableEntry::Sending:
return Send;
case AddressTableEntry::Receiving:
return Receive;
- default: break;
- }
+ case AddressTableEntry::Hidden:
+ return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
return QVariant();
}
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index a01a7bc386..6cb4a4c546 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -23,15 +23,13 @@ bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
- switch(column)
- {
+ switch (static_cast<BanTableModel::ColumnIndex>(column)) {
case BanTableModel::Address:
return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
case BanTableModel::Bantime:
return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
- }
-
- return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
// private implementation
@@ -119,16 +117,17 @@ QVariant BanTableModel::data(const QModelIndex &index, int role) const
CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());
+ const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole) {
- switch(index.column())
- {
+ switch (column) {
case Address:
return QString::fromStdString(rec->subnet.ToString());
case Bantime:
QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
date = date.addSecs(rec->banEntry.nBanUntil);
return QLocale::system().toString(date, QLocale::LongFormat);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
return QVariant();
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index bad74fcbcc..f25b415820 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -62,6 +62,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
+Q_IMPORT_PLUGIN(QMacStylePlugin);
#endif
#endif
@@ -464,9 +465,7 @@ int GuiMain(int argc, char* argv[])
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#if QT_VERSION >= 0x050600
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-#endif
#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS)
const auto os_name = QSysInfo::prettyProductName();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ccc0a6828c..6677c9e3b5 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -88,6 +88,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
}
+ setContextMenuPolicy(Qt::PreventContextMenu);
+
#ifdef ENABLE_WALLET
enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET
@@ -544,7 +546,6 @@ void BitcoinGUI::createToolBars()
{
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
appToolBar = toolbar;
- toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
toolbar->setMovable(false);
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addAction(overviewAction);
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 56813b2d19..b244bc94f2 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -71,14 +71,14 @@ ClientModel::~ClientModel()
int ClientModel::getNumConnections(unsigned int flags) const
{
- CConnman::NumConnections connections = CConnman::CONNECTIONS_NONE;
+ ConnectionDirection connections = ConnectionDirection::None;
if(flags == CONNECTIONS_IN)
- connections = CConnman::CONNECTIONS_IN;
+ connections = ConnectionDirection::In;
else if (flags == CONNECTIONS_OUT)
- connections = CConnman::CONNECTIONS_OUT;
+ connections = ConnectionDirection::Out;
else if (flags == CONNECTIONS_ALL)
- connections = CConnman::CONNECTIONS_ALL;
+ connections = ConnectionDirection::Both;
return m_node.getNodeCount(connections);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 9e828ce0a6..e45cafe48a 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1079,7 +1079,7 @@
<item row="1" column="0">
<widget class="QLabel" name="peerConnectionTypeLabel">
<property name="toolTip">
- <string>The direction and type of peer connection: %1</string>
+ <string>The direction and type of peer connection: %1</string>
</property>
<property name="text">
<string>Direction/Type</string>
@@ -1342,13 +1342,65 @@
</widget>
</item>
<item row="12" column="0">
+ <widget class="QLabel" name="peerLastBlockLabel">
+ <property name="toolTip">
+ <string>Elapsed time since a novel block passing initial validity checks was received from this peer.</string>
+ </property>
+ <property name="text">
+ <string>Last Block</string>
+ </property>
+ </widget>
+ </item>
+ <item row="12" column="1">
+ <widget class="QLabel" name="peerLastBlock">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="13" column="0">
+ <widget class="QLabel" name="peerLastTxLabel">
+ <property name="toolTip">
+ <string>Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
+ </property>
+ <property name="text">
+ <string>Last Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item row="13" column="1">
+ <widget class="QLabel" name="peerLastTx">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="14" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1364,14 +1416,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1387,14 +1439,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1410,14 +1462,14 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1433,14 +1485,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="18" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="18" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1456,7 +1508,7 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="19" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1466,7 +1518,7 @@
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="19" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1482,14 +1534,14 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="20" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="20" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1505,14 +1557,14 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="21" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="19" column="1">
+ <item row="21" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1528,7 +1580,7 @@
</property>
</widget>
</item>
- <item row="20" column="0">
+ <item row="22" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1538,7 +1590,7 @@
</property>
</widget>
</item>
- <item row="20" column="1">
+ <item row="22" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1554,7 +1606,7 @@
</property>
</widget>
</item>
- <item row="21" column="0">
+ <item row="23" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index b82143e1ba..f85de0811a 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -68,7 +68,7 @@
</property>
<property name="maximumSize">
<size>
- <width>30</width>
+ <width>45</width>
<height>16777215</height>
</size>
</property>
@@ -89,9 +89,6 @@
<height>24</height>
</size>
</property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
@@ -406,7 +403,7 @@
</property>
<property name="maximumSize">
<size>
- <width>30</width>
+ <width>45</width>
<height>16777215</height>
</size>
</property>
@@ -427,9 +424,6 @@
<height>24</height>
</size>
</property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index c70bd9f418..89d6deb70d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -59,6 +59,8 @@
#include <QUrlQuery>
#include <QtGlobal>
+#include <chrono>
+
#if defined(Q_OS_MAC)
#include <QProcess>
@@ -706,9 +708,11 @@ QString formatServicesStr(quint64 mask)
return QObject::tr("None");
}
-QString formatPingTime(int64_t ping_usec)
+QString formatPingTime(std::chrono::microseconds ping_time)
{
- return (ping_usec == std::numeric_limits<int64_t>::max() || ping_usec == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(ping_usec / 1000), 10));
+ return (ping_time == std::chrono::microseconds::max() || ping_time == 0us) ?
+ QObject::tr("N/A") :
+ QString(QObject::tr("%1 ms")).arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10));
}
QString formatTimeOffset(int64_t nTimeOffset)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 7984aa1141..6395ec6abd 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -20,6 +20,8 @@
#include <QString>
#include <QTableView>
+#include <chrono>
+
class QValidatedLineEdit;
class SendCoinsRecipient;
@@ -202,8 +204,8 @@ namespace GUIUtil
/** Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(quint64 mask);
- /** Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0 */
- QString formatPingTime(int64_t ping_usec);
+ /** Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0 */
+ QString formatPingTime(std::chrono::microseconds ping_time);
/** Format a CNodeCombinedStats.nTimeOffset into a user-readable string */
QString formatTimeOffset(int64_t nTimeOffset);
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 5f518a67cd..3459bf4cf8 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -23,8 +23,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
- switch(column)
- {
+ switch (static_cast<PeerTableModel::ColumnIndex>(column)) {
case PeerTableModel::NetNodeId:
return pLeft->nodeid < pRight->nodeid;
case PeerTableModel::Address:
@@ -34,16 +33,15 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
case PeerTableModel::Network:
return pLeft->m_network < pRight->m_network;
case PeerTableModel::Ping:
- return pLeft->m_min_ping_usec < pRight->m_min_ping_usec;
+ return pLeft->m_min_ping_time < pRight->m_min_ping_time;
case PeerTableModel::Sent:
return pLeft->nSendBytes < pRight->nSendBytes;
case PeerTableModel::Received:
return pLeft->nRecvBytes < pRight->nRecvBytes;
case PeerTableModel::Subversion:
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
- }
-
- return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
// private implementation
@@ -157,9 +155,9 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
+ const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole) {
- switch(index.column())
- {
+ switch (column) {
case NetNodeId:
return (qint64)rec->nodeStats.nodeid;
case Address:
@@ -170,26 +168,31 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
case Network:
return GUIUtil::NetworkToQString(rec->nodeStats.m_network);
case Ping:
- return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec);
+ return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_time);
case Sent:
return GUIUtil::formatBytes(rec->nodeStats.nSendBytes);
case Received:
return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes);
case Subversion:
return QString::fromStdString(rec->nodeStats.cleanSubVer);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
} else if (role == Qt::TextAlignmentRole) {
- switch (index.column()) {
- case ConnectionType:
- case Network:
- return QVariant(Qt::AlignCenter);
- case Ping:
- case Sent:
- case Received:
- return QVariant(Qt::AlignRight | Qt::AlignVCenter);
- default:
- return QVariant();
- }
+ switch (column) {
+ case NetNodeId:
+ case Address:
+ return {};
+ case ConnectionType:
+ case Network:
+ return QVariant(Qt::AlignCenter);
+ case Ping:
+ case Sent:
+ case Received:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+ case Subversion:
+ return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
} else if (role == StatsRole) {
switch (index.column()) {
case NetNodeId: return QVariant::fromValue(rec);
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 62adaa4e9f..61cb89d75a 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -45,9 +45,9 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu actions
QAction *copyURIAction = new QAction(tr("Copy URI"), this);
QAction* copyAddressAction = new QAction(tr("Copy address"), this);
- QAction *copyLabelAction = new QAction(tr("Copy label"), this);
- QAction *copyMessageAction = new QAction(tr("Copy message"), this);
- QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
+ copyLabelAction = new QAction(tr("Copy label"), this);
+ copyMessageAction = new QAction(tr("Copy message"), this);
+ copyAmountAction = new QAction(tr("Copy amount"), this);
// context menu
contextMenu = new QMenu(this);
@@ -81,7 +81,6 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
tableView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH);
tableView->horizontalHeader()->setStretchLastSection(true);
}
- tableView->horizontalHeader()->setSortIndicator(RecentRequestsTableModel::Date, Qt::DescendingOrder);
}
void ReceiveCoinsDialog::setModel(WalletModel *_model)
@@ -96,6 +95,8 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
QTableView* tableView = ui->recentRequestsView;
tableView->setModel(_model->getRecentRequestsTableModel());
+ tableView->sortByColumn(RecentRequestsTableModel::Date, Qt::DescendingOrder);
+
connect(tableView->selectionModel(),
&QItemSelectionModel::selectionChanged, this,
&ReceiveCoinsDialog::recentRequestsView_selectionChanged);
@@ -269,9 +270,18 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column)
// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point)
{
- if (!selectedRow().isValid()) {
+ const QModelIndex sel = selectedRow();
+ if (!sel.isValid()) {
return;
}
+
+ // disable context menu actions when appropriate
+ const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel();
+ const RecentRequestEntry& req = submodel->entry(sel.row());
+ copyLabelAction->setDisabled(req.recipient.label.isEmpty());
+ copyMessageAction->setDisabled(req.recipient.message.isEmpty());
+ copyAmountAction->setDisabled(req.recipient.amount == 0);
+
contextMenu->exec(QCursor::pos());
}
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index f12cd8ce0c..fbbccc5a33 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -53,6 +53,9 @@ private:
Ui::ReceiveCoinsDialog *ui;
WalletModel *model;
QMenu *contextMenu;
+ QAction* copyLabelAction;
+ QAction* copyMessageAction;
+ QAction* copyAmountAction;
const PlatformStyle *platformStyle;
QModelIndex selectedRow();
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 4a4b557acc..b258219894 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -474,9 +474,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
const QString hb_list{"<ul><li>\""
- + tr("To") + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\""
- + tr("From") + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\""
- + tr("No") + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"};
+ + ts.to + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\""
+ + ts.from + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\""
+ + ts.no + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"};
ui->peerHighBandwidthLabel->setToolTip(ui->peerHighBandwidthLabel->toolTip().arg(hb_list));
ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
@@ -619,10 +619,10 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create peer table context menu actions
QAction* disconnectAction = new QAction(tr("&Disconnect"), this);
- QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
- QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this);
- QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this);
- QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this);
+ QAction* banAction1h = new QAction(ts.ban_for + " " + tr("1 &hour"), this);
+ QAction* banAction24h = new QAction(ts.ban_for + " " + tr("1 &day"), this);
+ QAction* banAction7d = new QAction(ts.ban_for + " " + tr("1 &week"), this);
+ QAction* banAction365d = new QAction(ts.ban_for + " " + tr("1 &year"), this);
// create peer table context menu
peersTableContextMenu = new QMenu(this);
@@ -1114,26 +1114,29 @@ void RPCConsole::updateDetailWidget()
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
- ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? "Yes" : "No");
+ ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? ts.yes : ts.no);
QString bip152_hb_settings;
- if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings += "To";
- if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings == "" ? "From" : "/From");
- if (bip152_hb_settings == "") bip152_hb_settings = "No";
+ if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings = ts.to;
+ if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings.isEmpty() ? ts.from : QLatin1Char('/') + ts.from);
+ if (bip152_hb_settings.isEmpty()) bip152_hb_settings = ts.no;
ui->peerHighBandwidth->setText(bip152_hb_settings);
- ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never"));
- ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never"));
+ const int64_t time_now{GetSystemTimeInSeconds()};
+ ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.nTimeConnected));
+ ui->peerLastBlock->setText(TimeDurationField(time_now, stats->nodeStats.nLastBlockTime));
+ ui->peerLastTx->setText(TimeDurationField(time_now, stats->nodeStats.nLastTXTime));
+ ui->peerLastSend->setText(TimeDurationField(time_now, stats->nodeStats.nLastSend));
+ ui->peerLastRecv->setText(TimeDurationField(time_now, stats->nodeStats.nLastRecv));
ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
- ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
- ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_usec));
- ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec));
+ ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
+ ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true));
ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
if (stats->nodeStats.m_permissionFlags == PF_NONE) {
- ui->peerPermissions->setText(tr("N/A"));
+ ui->peerPermissions->setText(ts.na);
} else {
QStringList permissions;
for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) {
@@ -1141,25 +1144,25 @@ void RPCConsole::updateDetailWidget()
}
ui->peerPermissions->setText(permissions.join(" & "));
}
- ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A"));
+ ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : ts.na);
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
// Sync height is init to -1
- if (stats->nodeStateStats.nSyncHeight > -1)
+ if (stats->nodeStateStats.nSyncHeight > -1) {
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
- else
- ui->peerSyncHeight->setText(tr("Unknown"));
-
+ } else {
+ ui->peerSyncHeight->setText(ts.unknown);
+ }
// Common height is init to -1
- if (stats->nodeStateStats.nCommonHeight > -1)
+ if (stats->nodeStateStats.nCommonHeight > -1) {
ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight));
- else
- ui->peerCommonHeight->setText(tr("Unknown"));
-
+ } else {
+ ui->peerCommonHeight->setText(ts.unknown);
+ }
ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
- ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait_usec));
+ ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait));
}
ui->peersTabRightPanel->show();
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 5f308dc36d..b9806e40c9 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -136,6 +136,11 @@ Q_SIGNALS:
void cmdRequest(const QString &command, const WalletModel* wallet_model);
private:
+ struct TranslatedStrings {
+ const QString yes{tr("Yes")}, no{tr("No")}, to{tr("To")}, from{tr("From")},
+ ban_for{tr("Ban for")}, na{tr("N/A")}, unknown{tr("Unknown")};
+ } const ts;
+
void startExecutor();
void setTrafficGraphRange(int mins);
@@ -168,6 +173,11 @@ private:
/** Update UI with latest network info from model. */
void updateNetworkState();
+ /** Helper for the output of a time duration field. Inputs are UNIX epoch times. */
+ QString TimeDurationField(uint64_t time_now, uint64_t time_at_event) const {
+ return time_at_event ? GUIUtil::formatDurationStr(time_now - time_at_event) : tr("Never");
+ }
+
private Q_SLOTS:
void updateAlerts(const QString& warnings);
};
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 9ef5fe8fc7..a1baf6a402 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -54,6 +54,13 @@ int main(int argc, char* argv[])
NodeContext node_context;
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
+ gArgs.ForceSetArg("-listen", "0");
+ gArgs.ForceSetArg("-listenonion", "0");
+ gArgs.ForceSetArg("-discover", "0");
+ gArgs.ForceSetArg("-dnsseed", "0");
+ gArgs.ForceSetArg("-fixedseeds", "0");
+ gArgs.ForceSetArg("-upnp", "0");
+ gArgs.ForceSetArg("-natpmp", "0");
bool fInvalid = false;
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index e6d483364c..a7556eed04 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -526,27 +526,30 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return QVariant();
TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
- switch(role)
- {
+ const auto column = static_cast<ColumnIndex>(index.column());
+ switch (role) {
case RawDecorationRole:
- switch(index.column())
- {
+ switch (column) {
case Status:
return txStatusDecoration(rec);
case Watchonly:
return txWatchonlyDecoration(rec);
+ case Date: return {};
+ case Type: return {};
case ToAddress:
return txAddressDecoration(rec);
- }
- break;
+ case Amount: return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::DecorationRole:
{
QIcon icon = qvariant_cast<QIcon>(index.data(RawDecorationRole));
return platformStyle->TextColorIcon(icon);
}
case Qt::DisplayRole:
- switch(index.column())
- {
+ switch (column) {
+ case Status: return {};
+ case Watchonly: return {};
case Date:
return formatTxDate(rec);
case Type:
@@ -555,12 +558,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return formatTxToAddress(rec, false);
case Amount:
return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS);
- }
- break;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::EditRole:
// Edit role is used for sorting, so return the unformatted values
- switch(index.column())
- {
+ switch (column) {
case Status:
return QString::fromStdString(rec->status.sortKey);
case Date:
@@ -573,8 +575,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return formatTxToAddress(rec, true);
case Amount:
return qint64(rec->credit + rec->debit);
- }
- break;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::ToolTipRole:
return formatTooltip(rec);
case Qt::TextAlignmentRole:
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index b568f41158..f99a3e286d 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -160,7 +160,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
transactionView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH);
transactionView->horizontalHeader()->setStretchLastSection(true);
}
- transactionView->horizontalHeader()->setSortIndicator(TransactionTableModel::Date, Qt::DescendingOrder);
// Actions
abandonAction = new QAction(tr("Abandon transaction"), this);
@@ -172,6 +171,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this);
+ QAction *editLabelAction = new QAction(tr("Edit address label"), this);
QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
contextMenu = new QMenu(this);
@@ -186,6 +186,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu->addSeparator();
contextMenu->addAction(bumpFeeAction);
contextMenu->addAction(abandonAction);
+ contextMenu->addAction(editLabelAction);
connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseType);
@@ -206,6 +207,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(copyTxIDAction, &QAction::triggered, this, &TransactionView::copyTxID);
connect(copyTxHexAction, &QAction::triggered, this, &TransactionView::copyTxHex);
connect(copyTxPlainText, &QAction::triggered, this, &TransactionView::copyTxPlainText);
+ connect(editLabelAction, &QAction::triggered, this, &TransactionView::editLabel);
connect(showDetailsAction, &QAction::triggered, this, &TransactionView::showDetails);
// Double-clicking on a transaction on the transaction history page shows details
connect(this, &TransactionView::doubleClicked, this, &TransactionView::showDetails);
@@ -233,6 +235,7 @@ void TransactionView::setModel(WalletModel *_model)
transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
transactionProxyModel->setSortRole(Qt::EditRole);
transactionView->setModel(transactionProxyModel);
+ transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder);
if (_model->getOptionsModel())
{
@@ -474,6 +477,52 @@ void TransactionView::copyTxPlainText()
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxPlainTextRole);
}
+void TransactionView::editLabel()
+{
+ if(!transactionView->selectionModel() ||!model)
+ return;
+ QModelIndexList selection = transactionView->selectionModel()->selectedRows();
+ if(!selection.isEmpty())
+ {
+ AddressTableModel *addressBook = model->getAddressTableModel();
+ if(!addressBook)
+ return;
+ QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString();
+ if(address.isEmpty())
+ {
+ // If this transaction has no associated address, exit
+ return;
+ }
+ // Is address in address book? Address book can miss address when a transaction is
+ // sent from outside the UI.
+ int idx = addressBook->lookupAddress(address);
+ if(idx != -1)
+ {
+ // Edit sending / receiving address
+ QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex());
+ // Determine type of address, launch appropriate editor dialog type
+ QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
+
+ EditAddressDialog dlg(
+ type == AddressTableModel::Receive
+ ? EditAddressDialog::EditReceivingAddress
+ : EditAddressDialog::EditSendingAddress, this);
+ dlg.setModel(addressBook);
+ dlg.loadRow(idx);
+ dlg.exec();
+ }
+ else
+ {
+ // Add sending address
+ EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
+ this);
+ dlg.setModel(addressBook);
+ dlg.setAddress(address);
+ dlg.exec();
+ }
+ }
+}
+
void TransactionView::showDetails()
{
if(!transactionView->selectionModel())
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index cd40813461..35ada4aa7a 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -90,6 +90,7 @@ private Q_SLOTS:
void dateRangeChanged();
void showDetails();
void copyAddress();
+ void editLabel();
void copyLabel();
void copyAmount();
void copyTxID();
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c71d2cbe49..99182a3767 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1015,7 +1015,7 @@ static RPCHelpMan pruneblockchain()
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
- PruneBlockFilesManual(height);
+ PruneBlockFilesManual(::ChainstateActive(), height);
const CBlockIndex* block = ::ChainActive().Tip();
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
@@ -1073,9 +1073,9 @@ static RPCHelpMan gettxoutsetinfo()
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
- CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
+ CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB());
NodeContext& node = EnsureNodeContext(request.context);
- if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) {
+ if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1200,7 +1200,7 @@ static RPCHelpMan verifychain()
LOCK(cs_main);
- return CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+ return CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
},
};
}
@@ -1232,7 +1232,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
+ const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
switch (thresholdState) {
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
@@ -1246,12 +1246,12 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
+ int64_t since_height = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache);
bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
+ BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1562,7 +1562,7 @@ static RPCHelpMan preciousblock()
}
BlockValidationState state;
- PreciousBlock(state, Params(), pblockindex);
+ ::ChainstateActive().PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1598,7 +1598,7 @@ static RPCHelpMan invalidateblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
- InvalidateBlock(state, Params(), pblockindex);
+ ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
::ChainstateActive().ActivateBestChain(state, Params());
@@ -1637,7 +1637,7 @@ static RPCHelpMan reconsiderblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- ResetBlockFailureFlags(pblockindex);
+ ::ChainstateActive().ResetBlockFailureFlags(pblockindex);
}
BlockValidationState state;
@@ -2444,7 +2444,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil
chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&chainstate.CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
+ if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 50987a735b..62b9d1138c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -150,7 +150,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -358,7 +358,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -369,7 +369,7 @@ static RPCHelpMan generateblock()
// Add transactions
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
- RegenerateCommitments(block);
+ RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
{
LOCK(cs_main);
@@ -659,7 +659,7 @@ static RPCHelpMan getblocktemplate()
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (!Params().IsTestChain()) {
- if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
+ if (node.connman->GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
}
@@ -747,7 +747,7 @@ static RPCHelpMan getblocktemplate()
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 38a0bddddb..143be1274e 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -305,11 +305,11 @@ static RPCHelpMan verifymessage()
switch (MessageVerify(strAddress, strSign, strMessage)) {
case MessageVerificationResult::ERR_INVALID_ADDRESS:
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
+ throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding");
case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
case MessageVerificationResult::ERR_NOT_SIGNED:
return false;
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 0224ee697a..cf4d46cf2c 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -57,7 +57,7 @@ static RPCHelpMan getconnectioncount()
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+ return (int)node.connman->GetNodeCount(ConnectionDirection::Both);
},
};
}
@@ -202,14 +202,14 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
obj.pushKV("timeoffset", stats.nTimeOffset);
- if (stats.m_ping_usec > 0) {
- obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6);
+ if (stats.m_last_ping_time > 0us) {
+ obj.pushKV("pingtime", CountSecondsDouble(stats.m_last_ping_time));
}
- if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
- obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
+ if (stats.m_min_ping_time < std::chrono::microseconds::max()) {
+ obj.pushKV("minping", CountSecondsDouble(stats.m_min_ping_time));
}
- if (fStateStats && statestats.m_ping_wait_usec > 0) {
- obj.pushKV("pingwait", ((double)statestats.m_ping_wait_usec) / 1e6);
+ if (fStateStats && statestats.m_ping_wait > 0s) {
+ obj.pushKV("pingwait", CountSecondsDouble(statestats.m_ping_wait));
}
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
@@ -546,7 +546,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_I2P || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
@@ -630,9 +630,9 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
- obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
- obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
- obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(ConnectionDirection::Both));
+ obj.pushKV("connections_in", (int)node.connman->GetNodeCount(ConnectionDirection::In));
+ obj.pushKV("connections_out", (int)node.connman->GetNodeCount(ConnectionDirection::Out));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index ecac3b9e7e..20a4ce48b0 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1834,7 +1834,7 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
{
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
- //! The inner pubkey (x-only, so no Y coordinate parity).
+ //! The internal pubkey (x-only, so no Y coordinate parity).
const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
//! The output pubkey (taken from the scriptPubKey).
const XOnlyPubKey q{uint256(program)};
@@ -1852,9 +1852,9 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
- // Compute the tweak from the Merkle root and the inner pubkey.
+ // Compute the tweak from the Merkle root and the internal pubkey.
k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
- // Verify that the output pubkey matches the tweaked inner pubkey, after correcting for parity.
+ // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
return q.CheckPayToContract(p, k, control[0] & 1);
}
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 633a95ce96..04da10715f 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 0d480e35ea..5906913b58 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,6 +14,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <txorphanage.h>
#include <util/memory.h>
#include <util/string.h>
#include <util/system.h>
@@ -43,18 +44,6 @@ struct CConnmanTest : public CConnman {
}
};
-// Tests these internal-to-net_processing.cpp methods:
-extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
-extern void EraseOrphansFor(NodeId peer);
-extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
-
-struct COrphanTx {
- CTransactionRef tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
-};
-extern std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -295,15 +284,23 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
peerLogic->FinalizeNode(dummyNode, dummy);
}
-static CTransactionRef RandomOrphan()
+class TxOrphanageTest : public TxOrphanage
{
- std::map<uint256, COrphanTx>::iterator it;
- LOCK2(cs_main, g_cs_orphans);
- it = mapOrphanTransactions.lower_bound(InsecureRand256());
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- return it->second.tx;
-}
+public:
+ inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+ {
+ return m_orphans.size();
+ }
+
+ CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+ {
+ std::map<uint256, OrphanTx>::iterator it;
+ it = m_orphans.lower_bound(InsecureRand256());
+ if (it == m_orphans.end())
+ it = m_orphans.begin();
+ return it->second.tx;
+ }
+};
static void MakeNewKeyWithFastRandomContext(CKey& key)
{
@@ -323,11 +320,14 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
// signature's R and S values have leading zeros.
g_insecure_rand_ctx = FastRandomContext(ArithToUint256(arith_uint256(33)));
+ TxOrphanageTest orphanage;
CKey key;
MakeNewKeyWithFastRandomContext(key);
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
+ LOCK(g_cs_orphans);
+
// 50 orphan transactions:
for (int i = 0; i < 50; i++)
{
@@ -340,13 +340,13 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
- AddOrphanTx(MakeTransactionRef(tx), i);
+ orphanage.AddTx(MakeTransactionRef(tx), i);
}
// ... and 50 that depend on other orphans:
for (int i = 0; i < 50; i++)
{
- CTransactionRef txPrev = RandomOrphan();
+ CTransactionRef txPrev = orphanage.RandomOrphan();
CMutableTransaction tx;
tx.vin.resize(1);
@@ -357,13 +357,13 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
- AddOrphanTx(MakeTransactionRef(tx), i);
+ orphanage.AddTx(MakeTransactionRef(tx), i);
}
// This really-big orphan should be ignored:
for (int i = 0; i < 10; i++)
{
- CTransactionRef txPrev = RandomOrphan();
+ CTransactionRef txPrev = orphanage.RandomOrphan();
CMutableTransaction tx;
tx.vout.resize(1);
@@ -381,25 +381,24 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
for (unsigned int j = 1; j < tx.vin.size(); j++)
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
- BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i));
+ BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
}
- LOCK2(cs_main, g_cs_orphans);
// Test EraseOrphansFor:
for (NodeId i = 0; i < 3; i++)
{
- size_t sizeBefore = mapOrphanTransactions.size();
- EraseOrphansFor(i);
- BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore);
+ size_t sizeBefore = orphanage.CountOrphans();
+ orphanage.EraseForPeer(i);
+ BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
}
// Test LimitOrphanTxSize() function:
- LimitOrphanTxSize(40);
- BOOST_CHECK(mapOrphanTransactions.size() <= 40);
- LimitOrphanTxSize(10);
- BOOST_CHECK(mapOrphanTransactions.size() <= 10);
- LimitOrphanTxSize(0);
- BOOST_CHECK(mapOrphanTransactions.empty());
+ orphanage.LimitOrphans(40);
+ BOOST_CHECK(orphanage.CountOrphans() <= 40);
+ orphanage.LimitOrphans(10);
+ BOOST_CHECK(orphanage.CountOrphans() <= 10);
+ orphanage.LimitOrphans(0);
+ BOOST_CHECK(orphanage.CountOrphans() == 0);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/FuzzedDataProvider.h b/src/test/fuzz/FuzzedDataProvider.h
index 744a9d78ce..6cbfc39bc2 100644
--- a/src/test/fuzz/FuzzedDataProvider.h
+++ b/src/test/fuzz/FuzzedDataProvider.h
@@ -20,6 +20,7 @@
#include <cstdint>
#include <cstring>
#include <initializer_list>
+#include <limits>
#include <string>
#include <type_traits>
#include <utility>
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 1ea6b3d01d..b55f1c72b1 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
index 9ecd172e19..dbc0b5ab81 100644
--- a/src/test/fuzz/autofile.cpp
+++ b/src/test/fuzz/autofile.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index e0715f3e29..8bf484722c 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,6 +8,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <cstdint>
@@ -26,7 +27,7 @@ int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept
void initialize_banman()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(banman, initialize_banman)
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index d43c182644..c5bb8744a4 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index 3a1b2dbbe7..ffe38f10fc 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 8ece94d771..d951bda20f 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,6 +16,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <cstdint>
@@ -36,7 +37,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -263,7 +264,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats;
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 71b4b00116..ae77a45e44 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,6 +10,7 @@
#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 <cstdint>
@@ -17,7 +18,7 @@
void initialize_connman()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(connman, initialize_connman)
@@ -94,7 +95,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
(void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
},
[&] {
- (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL}));
+ (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({ConnectionDirection::None, ConnectionDirection::In, ConnectionDirection::Out, ConnectionDirection::Both}));
},
[&] {
connman.MarkAddressGood(random_address);
@@ -104,7 +105,9 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
},
[&] {
// Limit now to int32_t to avoid signed integer overflow
- (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>());
+ (void)connman.PoissonNextSendInbound(
+ std::chrono::microseconds{fuzzed_data_provider.ConsumeIntegral<int32_t>()},
+ std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<int>()});
},
[&] {
CSerializedNetMsg serialized_net_msg;
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 17ac48fca7..eeeac18968 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index bb8dd4594f..8adfa92420 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
index 0e1c44cded..bb4ef22158 100644
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index f3b6e6af04..473caec6ff 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,13 +7,14 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <vector>
void initialize_data_stream_addr_man()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index ba5f0c1a75..64c6e49615 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-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.
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index edb270d437..1fab46ff13 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-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.
@@ -44,7 +44,7 @@ void initialize()
std::get<1>(it->second)();
}
-#if defined(PROVIDE_MAIN_FUNCTION)
+#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
uint8_t buffer[1024];
@@ -71,7 +71,7 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
return 0;
}
-#if defined(PROVIDE_MAIN_FUNCTION)
+#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
int main(int argc, char** argv)
{
initialize();
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 4abc52c15a..2bad77bdc1 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-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.
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index ac83d91ea0..5bc99ddcb9 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -84,8 +84,7 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
(void)DecompressAmount(u64);
(void)FormatISO8601Date(i64);
(void)FormatISO8601DateTime(i64);
- // FormatMoney(i) not defined when i == std::numeric_limits<int64_t>::min()
- if (i64 != std::numeric_limits<int64_t>::min()) {
+ {
int64_t parsed_money;
if (ParseMoney(FormatMoney(i64), parsed_money)) {
assert(parsed_money == i64);
@@ -132,8 +131,7 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
(void)SipHashUint256Extra(u64, u64, u256, u32);
(void)ToLower(ch);
(void)ToUpper(ch);
- // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
- if (i64 != std::numeric_limits<int64_t>::min()) {
+ {
int64_t parsed_money;
if (ParseMoney(ValueFromAmount(i64).getValStr(), parsed_money)) {
assert(parsed_money == i64);
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index fa4024fc38..908e9a1c83 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index 95597bf082..dbd0c76d42 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,7 @@
void initialize_load_external_block_file()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp
index 23e0baa564..1eefd4c521 100644
--- a/src/test/fuzz/merkleblock.cpp
+++ b/src/test/fuzz/merkleblock.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index 2d761cef15..4ea9511870 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 21dca4eb05..b056f46f2e 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -22,7 +22,7 @@
void initialize_net()
{
- static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::MAIN);
+ static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN);
}
FUZZ_TARGET_INIT(net, initialize_net)
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index a42080eb66..f9d8129ca9 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index aaebe83c0a..606ebfc151 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -21,17 +21,17 @@ FUZZ_TARGET(node_eviction)
std::vector<NodeEvictionCandidate> eviction_candidates;
while (fuzzed_data_provider.ConsumeBool()) {
eviction_candidates.push_back({
- fuzzed_data_provider.ConsumeIntegral<NodeId>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
+ /* id */ fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ /* nTimeConnected */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* m_min_ping_time */ std::chrono::microseconds{fuzzed_data_provider.ConsumeIntegral<int64_t>()},
+ /* nLastBlockTime */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* nLastTXTime */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* fRelevantServices */ fuzzed_data_provider.ConsumeBool(),
+ /* fRelayTxes */ fuzzed_data_provider.ConsumeBool(),
+ /* fBloomFilter */ fuzzed_data_provider.ConsumeBool(),
+ /* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ /* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
+ /* m_is_local */ fuzzed_data_provider.ConsumeBool(),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index fff893fb3f..116b7a71d9 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <txmempool.h>
#include <cstdint>
@@ -16,7 +17,7 @@
void initialize_policy_estimator()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator)
diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp
index 73242870a0..9021d95954 100644
--- a/src/test/fuzz/policy_estimator_io.cpp
+++ b/src/test/fuzz/policy_estimator_io.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,13 +6,14 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <vector>
void initialize_policy_estimator_io()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(policy_estimator_io, initialize_policy_estimator_io)
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index c4348495bf..53726ca893 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 442e32d4ca..0289d49ccc 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -18,6 +18,7 @@
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
+#include <txorphanage.h>
#include <util/memory.h>
#include <validationinterface.h>
#include <version.h>
@@ -56,7 +57,7 @@ void initialize_process_message()
{
Assert(GetNumMsgTypes() == getAllNetMessageTypes().size()); // If this fails, add or remove the message type below
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index ef45196671..617a71ea60 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,6 +13,7 @@
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
+#include <txorphanage.h>
#include <util/memory.h>
#include <validation.h>
#include <validationinterface.h>
@@ -23,7 +24,7 @@ const TestingSetup* g_setup;
void initialize_process_messages()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 2a08b45aa3..07059cce76 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 7fadf36f98..193862e847 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2020 The Bitcoin Core developers
+// Copyright (c) 2019-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.
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index 8d9a939dab..5f07acbcc7 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
index bdbfe817ff..eb1c808a88 100644
--- a/src/test/fuzz/script_ops.cpp
+++ b/src/test/fuzz/script_ops.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
index bc4867839c..62ed50d13f 100644
--- a/src/test/fuzz/scriptnum_ops.cpp
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp
index 83effec064..303dcf13e3 100644
--- a/src/test/fuzz/signet.cpp
+++ b/src/test/fuzz/signet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,6 +10,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <optional>
@@ -17,7 +18,7 @@
void initialize_signet()
{
- static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::SIGNET);
+ static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::SIGNET);
}
FUZZ_TARGET_INIT(signet, initialize_signet)
diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp
new file mode 100644
index 0000000000..e5cc4cabe5
--- /dev/null
+++ b/src/test/fuzz/socks5.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2020-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <netbase.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace {
+int default_socks5_recv_timeout;
+};
+
+extern int g_socks5_recv_timeout;
+
+void initialize_socks5()
+{
+ static const auto testing_setup = MakeNoLogFileContext<const BasicTestingSetup>();
+ default_socks5_recv_timeout = g_socks5_recv_timeout;
+}
+
+FUZZ_TARGET_INIT(socks5, initialize_socks5)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ ProxyCredentials proxy_credentials;
+ proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512);
+ proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512);
+ InterruptSocks5(fuzzed_data_provider.ConsumeBool());
+ // Set FUZZED_SOCKET_FAKE_LATENCY=1 to exercise recv timeout code paths. This
+ // will slow down fuzzing.
+ g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1 : default_socks5_recv_timeout;
+ FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider);
+ // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within
+ // a few seconds of fuzzing.
+ (void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512),
+ fuzzed_data_provider.ConsumeIntegral<int>(),
+ fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr,
+ fuzzed_sock);
+}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index ec8a3b23db..93b4948a2f 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index b66a7abfb3..2c92b159a5 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 3621702e45..d9571209fa 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/torcontrol.cpp b/src/test/fuzz/torcontrol.cpp
new file mode 100644
index 0000000000..a97d3962bf
--- /dev/null
+++ b/src/test/fuzz/torcontrol.cpp
@@ -0,0 +1,80 @@
+// Copyright (c) 2020-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <torcontrol.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+class DummyTorControlConnection : public TorControlConnection
+{
+public:
+ DummyTorControlConnection() : TorControlConnection{nullptr}
+ {
+ }
+
+ bool Connect(const std::string&, const ConnectionCB&, const ConnectionCB&)
+ {
+ return true;
+ }
+
+ void Disconnect()
+ {
+ }
+
+ bool Command(const std::string&, const ReplyHandlerCB&)
+ {
+ return true;
+ }
+};
+
+void initialize_torcontrol()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+}
+
+FUZZ_TARGET_INIT(torcontrol, initialize_torcontrol)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ TorController tor_controller;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ TorControlReply tor_control_reply;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ tor_control_reply.code = 250;
+ },
+ [&] {
+ tor_control_reply.code = 510;
+ },
+ [&] {
+ tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
+ });
+ tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider);
+ if (tor_control_reply.lines.empty()) {
+ break;
+ }
+ DummyTorControlConnection dummy_tor_control_connection;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.auth_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.authchallenge_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.protocolinfo_cb(dummy_tor_control_connection, tor_control_reply);
+ });
+ }
+}
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index 13ae450756..41e1687405 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -100,16 +100,7 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
- bool skip_tx_to_univ = false;
- for (const CTxOut& txout : tx.vout) {
- if (txout.nValue == std::numeric_limits<int64_t>::min()) {
- skip_tx_to_univ = true;
- }
- }
- if (!skip_tx_to_univ) {
- TxToUniv(tx, /* hashBlock */ {}, u);
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
- TxToUniv(tx, u256_max, u);
- }
+ TxToUniv(tx, /* hashBlock */ {}, u);
+ static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ TxToUniv(tx, u256_max, u);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 7a2dcfe84a..d8c536e8b1 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-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.
@@ -10,6 +10,7 @@
#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
+#include <compat.h>
#include <consensus/consensus.h>
#include <merkleblock.h>
#include <net.h>
@@ -23,14 +24,13 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/net.h>
-#include <test/util/setup_common.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/time.h>
-#include <util/vector.h>
#include <version.h>
#include <algorithm>
+#include <array>
#include <cstdint>
#include <cstdio>
#include <optional>
@@ -251,6 +251,15 @@ template <class T>
}
/**
+ * Sets errno to a value selected from the given std::array `errnos`.
+ */
+template <typename T, size_t size>
+void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array<T, size>& errnos)
+{
+ errno = fuzzed_data_provider.PickValueInArray(errnos);
+}
+
+/**
* Returns a byte vector of specified size regardless of the number of remaining bytes available
* from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
*/
@@ -324,19 +333,6 @@ inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, co
void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept;
-template <class T = const BasicTestingSetup>
-std::unique_ptr<T> MakeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {})
-{
- // Prepend default arguments for fuzzing
- const std::vector<const char*> arguments = Cat(
- {
- "-nodebuglogfile",
- },
- extra_args);
-
- return MakeUnique<T>(chain_name, arguments);
-}
-
class FuzzedFileProvider
{
FuzzedDataProvider& m_fuzzed_data_provider;
@@ -534,4 +530,127 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
}
}
+class FuzzedSock : public Sock
+{
+ FuzzedDataProvider& m_fuzzed_data_provider;
+
+public:
+ explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
+ {
+ }
+
+ ~FuzzedSock() override
+ {
+ }
+
+ FuzzedSock& operator=(Sock&& other) override
+ {
+ assert(false && "Not implemented yet.");
+ return *this;
+ }
+
+ SOCKET Get() const override
+ {
+ assert(false && "Not implemented yet.");
+ return INVALID_SOCKET;
+ }
+
+ SOCKET Release() override
+ {
+ assert(false && "Not implemented yet.");
+ return INVALID_SOCKET;
+ }
+
+ void Reset() override
+ {
+ assert(false && "Not implemented yet.");
+ }
+
+ ssize_t Send(const void* data, size_t len, int flags) const override
+ {
+ constexpr std::array send_errnos{
+ EACCES,
+ EAGAIN,
+ EALREADY,
+ EBADF,
+ ECONNRESET,
+ EDESTADDRREQ,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ EISCONN,
+ EMSGSIZE,
+ ENOBUFS,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EOPNOTSUPP,
+ EPIPE,
+ EWOULDBLOCK,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return len;
+ }
+ const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
+ }
+ return r;
+ }
+
+ ssize_t Recv(void* buf, size_t len, int flags) const override
+ {
+ constexpr std::array recv_errnos{
+ EAGAIN,
+ EBADF,
+ ECONNREFUSED,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EWOULDBLOCK,
+ };
+ assert(buf != nullptr || len == 0);
+ if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ const std::vector<uint8_t> random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
+ m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
+ if (random_bytes.empty()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ std::memcpy(buf, random_bytes.data(), random_bytes.size());
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ if (len > random_bytes.size()) {
+ std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
+ }
+ return len;
+ }
+ if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
+ std::this_thread::sleep_for(std::chrono::milliseconds{2});
+ }
+ return random_bytes.size();
+ }
+
+ bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
+ {
+ return m_fuzzed_data_provider.ConsumeBool();
+ }
+};
+
+[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
+{
+ return FuzzedSock{fuzzed_data_provider};
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index aa628371e6..9acd17c463 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -122,7 +122,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
uint256 hashHighFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
@@ -143,7 +143,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
uint256 hashLowFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
@@ -157,7 +157,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
@@ -179,7 +179,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
uint256 hashLowFeeTx2 = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
// Verify that this tx isn't selected.
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -192,7 +192,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
@@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
fCheckpointsEnabled = false;
// Simple block creation, nothing special yet:
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// We can't make transactions until we have inputs
// Therefore, load 110 blocks :)
@@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
LOCK(m_node.mempool->cs);
// Just to make sure we can still make simple blocks
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
const CAmount BLOCKSUBSIDY = 50*COIN;
const CAmount LOWFEE = CENT;
@@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
m_node.mempool->clear();
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
@@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// block size > limit
@@ -311,13 +311,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// orphan in *m_node.mempool, template creation fails
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
m_node.mempool->clear();
// child with higher feerate than parent
@@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// coinbase in *m_node.mempool, template creation fails
@@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// give it a fee so it'll get mined
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-cb-multiple
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
m_node.mempool->clear();
// double spend txn pair in *m_node.mempool, template creation fails
@@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
m_node.mempool->clear();
// subsidy changing
@@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
next->BuildSkip();
::ChainActive().SetTip(next);
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// Extend to a 210000-long block chain.
while (::ChainActive().Tip()->nHeight < 210000) {
CBlockIndex* prev = ::ChainActive().Tip();
@@ -387,7 +387,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
next->BuildSkip();
::ChainActive().SetTip(next);
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// invalid p2sh txn in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw block-validation-failed
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
m_node.mempool->clear();
// Delete the dummy blocks again.
@@ -492,7 +492,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// None of the of the absolute height/time locked tx should have made
// it into the template because we still check IsFinalTx in CreateNewBlock,
@@ -505,7 +505,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
::ChainActive().Tip()->nHeight++;
SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
::ChainActive().Tip()->nHeight--;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 1c7c35528e..a29c6a2665 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -322,6 +322,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
@@ -332,6 +333,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
@@ -352,6 +354,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
// TOR, invalid base32
BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
+ // I2P
+ const char* i2p_addr = "UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P";
+ BOOST_REQUIRE(addr.SetSpecial(i2p_addr));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsI2P());
+
+ BOOST_CHECK(!addr.IsTor());
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), ToLower(i2p_addr));
+
+ // I2P, correct length, but decodes to less than the expected number of bytes.
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jn=.b32.i2p"));
+
+ // I2P, extra unnecessary padding
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna=.b32.i2p"));
+
+ // I2P, malicious
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v\0wtf.b32.i2p"s));
+
+ // I2P, valid but unsupported (56 Base32 characters)
+ // See "Encrypted LS with Base 32 Addresses" in
+ // https://geti2p.net/spec/encryptedleaseset.txt
+ BOOST_CHECK(
+ !addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.b32.i2p"));
+
+ // I2P, invalid base32
+ BOOST_CHECK(!addr.SetSpecial(std::string{"tp*szydbh4dp.b32.i2p"}));
+
// Internal
addr.SetInternal("esffpp");
BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid
@@ -772,21 +803,6 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
BOOST_CHECK_EQUAL(IsLocal(addr), false);
}
-BOOST_AUTO_TEST_CASE(PoissonNextSend)
-{
- g_mock_deterministic_tests = true;
-
- int64_t now = 5000;
- int average_interval_seconds = 600;
-
- auto poisson = ::PoissonNextSend(now, average_interval_seconds);
- std::chrono::microseconds poisson_chrono = ::PoissonNextSend(std::chrono::microseconds{now}, std::chrono::seconds{average_interval_seconds});
-
- BOOST_CHECK_EQUAL(poisson, poisson_chrono.count());
-
- g_mock_deterministic_tests = false;
-}
-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
{
std::vector<NodeEvictionCandidate> candidates;
@@ -794,7 +810,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
candidates.push_back({
/* id */ id,
/* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
- /* m_min_ping_time */ static_cast<int64_t>(random_context.randrange(100)),
+ /* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
/* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
/* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
/* fRelevantServices */ random_context.randbool(),
@@ -854,7 +870,7 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
// from eviction.
BOOST_CHECK(!IsEvicted(
number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.m_min_ping_time = candidate.id;
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
},
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
@@ -900,10 +916,10 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
// Combination of all tests above.
BOOST_CHECK(!IsEvicted(
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.m_min_ping_time = candidate.id; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index b54cbb3f00..810665877d 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -174,6 +174,16 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000000).write(), "0.00000100");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000000).write(), "0.00000010");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001");
+
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max()).write(), "92233720368.54775807");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 1).write(), "92233720368.54775806");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 2).write(), "92233720368.54775805");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 3).write(), "92233720368.54775804");
+ // ...
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 3).write(), "-92233720368.54775805");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 2).write(), "-92233720368.54775806");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 1).write(), "-92233720368.54775807");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min()).write(), "-92233720368.54775808");
}
static UniValue ValueFromString(const std::string &str)
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index d1770f750f..8f1d99b199 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -63,7 +63,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
unsigned int ParseScriptFlags(std::string strFlags)
{
- if (strFlags.empty() | strFlags == "NONE") return 0;
+ if (strFlags.empty() || strFlags == "NONE") return 0;
unsigned int flags = 0;
std::vector<std::string> words;
boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 0c6487fbfa..ba1edba0ae 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -42,7 +42,7 @@ std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coi
{
auto block = std::make_shared<CBlock>(
BlockAssembler{*Assert(node.mempool), Params()}
- .CreateNewBlock(coinbase_scriptPubKey)
+ .CreateNewBlock(::ChainstateActive(), coinbase_scriptPubKey)
->block);
LOCK(cs_main);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 1ffe435531..f2174cd084 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -179,7 +179,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
assert(!::ChainstateActive().CanFlushToDisk());
::ChainstateActive().InitCoinsCache(1 << 23);
assert(::ChainstateActive().CanFlushToDisk());
- if (!LoadGenesisBlock(chainparams)) {
+ if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
@@ -245,13 +245,13 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
- RegenerateCommitments(block);
+ RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 33f24e7c44..7323f1f0b6 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -15,6 +15,7 @@
#include <txmempool.h>
#include <util/check.h>
#include <util/string.h>
+#include <util/vector.h>
#include <type_traits>
#include <vector>
@@ -152,6 +153,23 @@ struct TestChain100DeterministicSetup : public TestChain100Setup {
TestChain100DeterministicSetup() : TestChain100Setup(true) { }
};
+/**
+ * Make a test setup that has disk access to the debug.log file disabled. Can
+ * be used in "hot loops", for example fuzzing or benchmarking.
+ */
+template <class T = const BasicTestingSetup>
+std::unique_ptr<T> MakeNoLogFileContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {})
+{
+ const std::vector<const char*> arguments = Cat(
+ {
+ "-nodebuglogfile",
+ "-nodebug",
+ },
+ extra_args);
+
+ return std::make_unique<T>(chain_name, arguments);
+}
+
class CTxMemPoolEntry;
struct TestMemPoolEntryHelper
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 845854bd4b..5a46002a79 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1180,6 +1180,16 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000), "0.000001");
BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000), "0.0000001");
BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000), "0.00000001");
+
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max()), "92233720368.54775807");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 1), "92233720368.54775806");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 2), "92233720368.54775805");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 3), "92233720368.54775804");
+ // ...
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 3), "-92233720368.54775805");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 2), "-92233720368.54775806");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 1), "-92233720368.54775807");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min()), "-92233720368.54775808");
}
BOOST_AUTO_TEST_CASE(util_ParseMoney)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 0c87c4d360..f3fc83078f 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -63,7 +63,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
- auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(CScript{} << i++ << OP_TRUE);
+ auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), CScript{} << i++ << OP_TRUE);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
@@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
{
CScript pubKey;
pubKey << 1 << OP_TRUE;
- auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
+ auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), pubKey);
CBlock pblock = ptemplate->block;
CTxOut witness;
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 50444f7bbe..8841a540f2 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -14,6 +14,18 @@
/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */
static int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
+static const std::string StateName(ThresholdState state)
+{
+ switch (state) {
+ case ThresholdState::DEFINED: return "DEFINED";
+ case ThresholdState::STARTED: return "STARTED";
+ case ThresholdState::LOCKED_IN: return "LOCKED_IN";
+ case ThresholdState::ACTIVE: return "ACTIVE";
+ case ThresholdState::FAILED: return "FAILED";
+ } // no default case, so the compiler can warn about missing cases
+ return "";
+}
+
static const Consensus::Params paramsDummy = Consensus::Params();
class TestConditionChecker : public AbstractThresholdConditionChecker
@@ -38,6 +50,13 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; }
};
+class TestNeverActiveConditionChecker : public TestConditionChecker
+{
+public:
+ int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
+ int64_t EndTime(const Consensus::Params& params) const override { return 1230768000; }
+};
+
#define CHECKERS 6
class VersionBitsTester
@@ -51,6 +70,8 @@ class VersionBitsTester
TestConditionChecker checker[CHECKERS];
// Another 6 that assume always active activation
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
+ // Another 6 that assume never active activation
+ TestNeverActiveConditionChecker checker_never[CHECKERS];
// Test counter (to identify failures)
int num;
@@ -65,6 +86,7 @@ public:
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
checker_always[i] = TestAlwaysActiveConditionChecker();
+ checker_never[i] = TestNeverActiveConditionChecker();
}
vpblock.clear();
return *this;
@@ -92,66 +114,40 @@ public:
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
- }
- }
- num++;
- return *this;
- }
- VersionBitsTester& TestDefined() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ // never active may go from DEFINED -> FAILED at the first period
+ const auto never_height = checker_never[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back());
+ BOOST_CHECK_MESSAGE(never_height == 0 || never_height == checker_never[i].Period(paramsDummy), strprintf("Test %i for StateSinceHeight (never active)", num));
}
}
num++;
return *this;
}
- VersionBitsTester& TestStarted() {
+ VersionBitsTester& TestState(ThresholdState exp) {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::STARTED, strprintf("Test %i for STARTED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
+ ThresholdState got = checker[i].GetStateFor(pindex);
+ ThresholdState got_always = checker_always[i].GetStateFor(pindex);
+ ThresholdState got_never = checker_never[i].GetStateFor(pindex);
+ // nHeight of the next block. If vpblock is empty, the next (ie first)
+ // block should be the genesis block with nHeight == 0.
+ int height = pindex == nullptr ? 0 : pindex->nHeight + 1;
+ BOOST_CHECK_MESSAGE(got == exp, strprintf("Test %i for %s height %d (got %s)", num, StateName(exp), height, StateName(got)));
+ BOOST_CHECK_MESSAGE(got_always == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE height %d (got %s; always active case)", num, height, StateName(got_always)));
+ BOOST_CHECK_MESSAGE(got_never == ThresholdState::DEFINED|| got_never == ThresholdState::FAILED, strprintf("Test %i for DEFINED/FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
}
}
num++;
return *this;
}
- VersionBitsTester& TestLockedIn() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
-
- VersionBitsTester& TestActive() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
-
- VersionBitsTester& TestFailed() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::FAILED, strprintf("Test %i for FAILED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
+ VersionBitsTester& TestDefined() { return TestState(ThresholdState::DEFINED); }
+ VersionBitsTester& TestStarted() { return TestState(ThresholdState::STARTED); }
+ VersionBitsTester& TestLockedIn() { return TestState(ThresholdState::LOCKED_IN); }
+ VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
+ VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; }
};
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 605c77fc3a..6666e49a2b 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -12,6 +12,7 @@
#include <net.h>
#include <netaddress.h>
#include <netbase.h>
+#include <util/readwritefile.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/time.h>
@@ -55,77 +56,6 @@ static const int MAX_LINE_LENGTH = 100000;
/****** Low-level TorControlConnection ********/
-/** Reply from Tor, can be single or multi-line */
-class TorControlReply
-{
-public:
- TorControlReply() { Clear(); }
-
- int code;
- std::vector<std::string> lines;
-
- void Clear()
- {
- code = 0;
- lines.clear();
- }
-};
-
-/** Low-level handling for Tor control connection.
- * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
- */
-class TorControlConnection
-{
-public:
- typedef std::function<void(TorControlConnection&)> ConnectionCB;
- typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
-
- /** Create a new TorControlConnection.
- */
- explicit TorControlConnection(struct event_base *base);
- ~TorControlConnection();
-
- /**
- * Connect to a Tor control port.
- * tor_control_center is address of the form host:port.
- * connected is the handler that is called when connection is successfully established.
- * disconnected is a handler that is called when the connection is broken.
- * Return true on success.
- */
- bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
-
- /**
- * Disconnect from Tor control port.
- */
- void Disconnect();
-
- /** Send a command, register a handler for the reply.
- * A trailing CRLF is automatically added.
- * Return true on success.
- */
- bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
-
- /** Response handlers for async replies */
- boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler;
-private:
- /** Callback when ready for use */
- std::function<void(TorControlConnection&)> connected;
- /** Callback when connection lost */
- std::function<void(TorControlConnection&)> disconnected;
- /** Libevent event base */
- struct event_base *base;
- /** Connection to control socket */
- struct bufferevent *b_conn;
- /** Message being received */
- TorControlReply message;
- /** Response handlers */
- std::deque<ReplyHandlerCB> reply_handlers;
-
- /** Libevent handlers: internal */
- static void readcb(struct bufferevent *bev, void *ctx);
- static void eventcb(struct bufferevent *bev, short what, void *ctx);
-};
-
TorControlConnection::TorControlConnection(struct event_base *_base):
base(_base), b_conn(nullptr)
{
@@ -362,101 +292,6 @@ std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
return mapping;
}
-/** Read full contents of a file and return them in a std::string.
- * Returns a pair <status, string>.
- * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
- *
- * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
- * (with len > maxsize) will be returned.
- */
-static std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
-{
- FILE *f = fsbridge::fopen(filename, "rb");
- if (f == nullptr)
- return std::make_pair(false,"");
- std::string retval;
- char buffer[128];
- size_t n;
- while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
- // Check for reading errors so we don't return any data if we couldn't
- // read the entire file (or up to maxsize)
- if (ferror(f)) {
- fclose(f);
- return std::make_pair(false,"");
- }
- retval.append(buffer, buffer+n);
- if (retval.size() > maxsize)
- break;
- }
- fclose(f);
- return std::make_pair(true,retval);
-}
-
-/** Write contents of std::string to a file.
- * @return true on success.
- */
-static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
-{
- FILE *f = fsbridge::fopen(filename, "wb");
- if (f == nullptr)
- return false;
- if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
- fclose(f);
- return false;
- }
- fclose(f);
- return true;
-}
-
-/****** Bitcoin specific TorController implementation ********/
-
-/** Controller that connects to Tor control socket, authenticate, then create
- * and maintain an ephemeral onion service.
- */
-class TorController
-{
-public:
- TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
- ~TorController();
-
- /** Get name of file to store private key in */
- fs::path GetPrivateKeyFile();
-
- /** Reconnect, after getting disconnected */
- void Reconnect();
-private:
- struct event_base* base;
- const std::string m_tor_control_center;
- TorControlConnection conn;
- std::string private_key;
- std::string service_id;
- bool reconnect;
- struct event *reconnect_ev;
- float reconnect_timeout;
- CService service;
- const CService m_target;
- /** Cookie for SAFECOOKIE auth */
- std::vector<uint8_t> cookie;
- /** ClientNonce for SAFECOOKIE auth */
- std::vector<uint8_t> clientNonce;
-
- /** Callback for ADD_ONION result */
- void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for AUTHENTICATE result */
- void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for AUTHCHALLENGE result */
- void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for PROTOCOLINFO result */
- void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback after successful connection */
- void connected_cb(TorControlConnection& conn);
- /** Callback after connection lost or failed connection attempt */
- void disconnected_cb(TorControlConnection& conn);
-
- /** Callback for reconnect timer */
- static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
-};
-
TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
base(_base),
m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0),
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 00f19db6ae..7258f27cb6 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -8,7 +8,19 @@
#ifndef BITCOIN_TORCONTROL_H
#define BITCOIN_TORCONTROL_H
+#include <fs.h>
+#include <netaddress.h>
+
+#include <boost/signals2/signal.hpp>
+
+#include <event2/bufferevent.h>
+#include <event2/event.h>
+
+#include <cstdlib>
+#include <deque>
+#include <functional>
#include <string>
+#include <vector>
class CService;
@@ -21,4 +33,128 @@ void StopTorControl();
CService DefaultOnionServiceTarget();
+/** Reply from Tor, can be single or multi-line */
+class TorControlReply
+{
+public:
+ TorControlReply() { Clear(); }
+
+ int code;
+ std::vector<std::string> lines;
+
+ void Clear()
+ {
+ code = 0;
+ lines.clear();
+ }
+};
+
+/** Low-level handling for Tor control connection.
+ * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
+ */
+class TorControlConnection
+{
+public:
+ typedef std::function<void(TorControlConnection&)> ConnectionCB;
+ typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
+
+ /** Create a new TorControlConnection.
+ */
+ explicit TorControlConnection(struct event_base *base);
+ ~TorControlConnection();
+
+ /**
+ * Connect to a Tor control port.
+ * tor_control_center is address of the form host:port.
+ * connected is the handler that is called when connection is successfully established.
+ * disconnected is a handler that is called when the connection is broken.
+ * Return true on success.
+ */
+ bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
+
+ /**
+ * Disconnect from Tor control port.
+ */
+ void Disconnect();
+
+ /** Send a command, register a handler for the reply.
+ * A trailing CRLF is automatically added.
+ * Return true on success.
+ */
+ bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
+
+ /** Response handlers for async replies */
+ boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler;
+private:
+ /** Callback when ready for use */
+ std::function<void(TorControlConnection&)> connected;
+ /** Callback when connection lost */
+ std::function<void(TorControlConnection&)> disconnected;
+ /** Libevent event base */
+ struct event_base *base;
+ /** Connection to control socket */
+ struct bufferevent *b_conn;
+ /** Message being received */
+ TorControlReply message;
+ /** Response handlers */
+ std::deque<ReplyHandlerCB> reply_handlers;
+
+ /** Libevent handlers: internal */
+ static void readcb(struct bufferevent *bev, void *ctx);
+ static void eventcb(struct bufferevent *bev, short what, void *ctx);
+};
+
+/****** Bitcoin specific TorController implementation ********/
+
+/** Controller that connects to Tor control socket, authenticate, then create
+ * and maintain an ephemeral onion service.
+ */
+class TorController
+{
+public:
+ TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
+ TorController() : conn{nullptr} {
+ // Used for testing only.
+ }
+ ~TorController();
+
+ /** Get name of file to store private key in */
+ fs::path GetPrivateKeyFile();
+
+ /** Reconnect, after getting disconnected */
+ void Reconnect();
+private:
+ struct event_base* base;
+ const std::string m_tor_control_center;
+ TorControlConnection conn;
+ std::string private_key;
+ std::string service_id;
+ bool reconnect;
+ struct event *reconnect_ev = nullptr;
+ float reconnect_timeout;
+ CService service;
+ const CService m_target;
+ /** Cookie for SAFECOOKIE auth */
+ std::vector<uint8_t> cookie;
+ /** ClientNonce for SAFECOOKIE auth */
+ std::vector<uint8_t> clientNonce;
+
+public:
+ /** Callback for ADD_ONION result */
+ void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for AUTHENTICATE result */
+ void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for AUTHCHALLENGE result */
+ void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for PROTOCOLINFO result */
+ void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback after successful connection */
+ void connected_cb(TorControlConnection& conn);
+ /** Callback after connection lost or failed connection attempt */
+ void disconnected_cb(TorControlConnection& conn);
+
+ /** Callback for reconnect timer */
+ static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
+};
+
#endif /* BITCOIN_TORCONTROL_H */
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 899835019a..f10b4ad740 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -23,7 +23,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe
int64_t _nTime, unsigned int _entryHeight,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp)
: tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight),
- spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), m_epoch(0)
+ spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{
nCountWithDescendants = 1;
nSizeWithDescendants = GetTxSize();
@@ -132,7 +132,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// include them, and update their CTxMemPoolEntry::m_parents to include this tx.
// we cache the in-mempool children to avoid duplicate updates
{
- const auto epoch = GetFreshEpoch();
+ WITH_FRESH_EPOCH(m_epoch);
for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
const uint256 &childHash = iter->second->GetHash();
txiter childIter = mapTx.find(childHash);
@@ -619,7 +619,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
}
-void CTxMemPool::check(const CCoinsViewCache *pcoins) const
+void CTxMemPool::check(CChainState& active_chainstate) const
{
if (m_check_ratio == 0) return;
@@ -633,8 +633,11 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
CAmount check_total_fee{0};
uint64_t innerUsage = 0;
- CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
- const int64_t spendheight = g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate);
+ CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
+ CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
+ const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
+ assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
@@ -655,7 +658,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
fDependsWait = true;
setParentCheck.insert(*it2);
} else {
- assert(pcoins->HaveCoin(txin.prevout));
+ assert(active_coins_tip.HaveCoin(txin.prevout));
}
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
@@ -1119,22 +1122,3 @@ void CTxMemPool::SetIsLoaded(bool loaded)
LOCK(cs);
m_is_loaded = loaded;
}
-
-
-CTxMemPool::EpochGuard CTxMemPool::GetFreshEpoch() const
-{
- return EpochGuard(*this);
-}
-CTxMemPool::EpochGuard::EpochGuard(const CTxMemPool& in) : pool(in)
-{
- assert(!pool.m_has_epoch_guard);
- ++pool.m_epoch;
- pool.m_has_epoch_guard = true;
-}
-
-CTxMemPool::EpochGuard::~EpochGuard()
-{
- // prevents stale results being used
- ++pool.m_epoch;
- pool.m_has_epoch_guard = false;
-}
diff --git a/src/txmempool.h b/src/txmempool.h
index b8de326737..8a96294192 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -21,6 +21,7 @@
#include <primitives/transaction.h>
#include <random.h>
#include <sync.h>
+#include <util/epochguard.h>
#include <util/hasher.h>
#include <boost/multi_index_container.hpp>
@@ -64,6 +65,7 @@ struct CompareIteratorByHash {
return a->GetTx().GetHash() < b->GetTx().GetHash();
}
};
+
/** \class CTxMemPoolEntry
*
* CTxMemPoolEntry stores data about the corresponding transaction, as well
@@ -156,7 +158,7 @@ public:
Children& GetMemPoolChildren() const { return m_children; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
- mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms
+ 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.
@@ -486,8 +488,7 @@ private:
mutable int64_t lastRollingFeeUpdate;
mutable bool blockSinceLastRollingFeeBump;
mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially
- mutable uint64_t m_epoch{0};
- mutable bool m_has_epoch_guard{false};
+ mutable Epoch m_epoch GUARDED_BY(cs);
// In-memory counter for external mempool tracking purposes.
// This number is incremented once every time a transaction
@@ -604,7 +605,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing.
*/
- void check(const CCoinsViewCache *pcoins) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void check(CChainState& active_chainstate) 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
@@ -666,7 +667,7 @@ public:
* for). Note: vHashesToUpdate should be the set of transactions from the
* disconnected block that have been accepted back into the mempool.
*/
- void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
/** Try to calculate all in-mempool ancestors of entry.
* (these are all calculated including the tx itself)
@@ -827,52 +828,22 @@ private:
*/
void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
- /** EpochGuard: RAII-style guard for using epoch-based graph traversal algorithms.
- * When walking ancestors or descendants, we generally want to avoid
- * visiting the same transactions twice. Some traversal algorithms use
- * std::set (or setEntries) to deduplicate the transaction we visit.
- * However, use of std::set is algorithmically undesirable because it both
- * adds an asymptotic factor of O(log n) to traverals cost and triggers O(n)
- * more dynamic memory allocations.
- * In many algorithms we can replace std::set with an internal mempool
- * counter to track the time (or, "epoch") that we began a traversal, and
- * check + update a per-transaction epoch for each transaction we look at to
- * determine if that transaction has not yet been visited during the current
- * traversal's epoch.
- * Algorithms using std::set can be replaced on a one by one basis.
- * Both techniques are not fundamentally incompatible across the codebase.
- * Generally speaking, however, the remaining use of std::set for mempool
- * traversal should be viewed as a TODO for replacement with an epoch based
- * traversal, rather than a preference for std::set over epochs in that
- * algorithm.
- */
- class EpochGuard {
- const CTxMemPool& pool;
- public:
- explicit EpochGuard(const CTxMemPool& in);
- ~EpochGuard();
- };
- // N.B. GetFreshEpoch modifies mutable state via the EpochGuard construction
- // (and later destruction)
- EpochGuard GetFreshEpoch() const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
/** visited marks a CTxMemPoolEntry as having been traversed
- * during the lifetime of the most recently created EpochGuard
+ * during the lifetime of the most recently created Epoch::Guard
* and returns false if we are the first visitor, true otherwise.
*
- * An EpochGuard must be held when visited is called or an assert will be
+ * An Epoch::Guard must be held when visited is called or an assert will be
* triggered.
*
*/
- bool visited(txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
- assert(m_has_epoch_guard);
- bool ret = it->m_epoch >= m_epoch;
- it->m_epoch = std::max(it->m_epoch, m_epoch);
- return ret;
+ bool visited(const txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch)
+ {
+ return m_epoch.visited(it->m_epoch_marker);
}
- bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
- assert(m_has_epoch_guard);
+ bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch)
+ {
+ assert(m_epoch.guarded()); // verify guard even when it==nullopt
return !it || visited(*it);
}
};
diff --git a/src/txorphanage.cpp b/src/txorphanage.cpp
new file mode 100644
index 0000000000..ed4783f1a5
--- /dev/null
+++ b/src/txorphanage.cpp
@@ -0,0 +1,202 @@
+// 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 <txorphanage.h>
+
+#include <consensus/validation.h>
+#include <logging.h>
+#include <policy/policy.h>
+
+#include <cassert>
+
+/** Expiration time for orphan transactions in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
+/** Minimum time between orphan transactions expire time checks in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+
+RecursiveMutex g_cs_orphans;
+
+bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ const uint256& hash = tx->GetHash();
+ if (m_orphans.count(hash))
+ return false;
+
+ // Ignore big transactions, to avoid a
+ // send-big-orphans memory exhaustion attack. If a peer has a legitimate
+ // large transaction with a missing parent then we assume
+ // it will rebroadcast it later, after the parent transaction(s)
+ // have been mined or received.
+ // 100 orphans, each of which is at most 100,000 bytes big is
+ // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
+ unsigned int sz = GetTransactionWeight(*tx);
+ if (sz > MAX_STANDARD_TX_WEIGHT)
+ {
+ LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
+ return false;
+ }
+
+ auto ret = m_orphans.emplace(hash, OrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
+ assert(ret.second);
+ m_orphan_list.push_back(ret.first);
+ // Allow for lookups in the orphan pool by wtxid, as well as txid
+ m_wtxid_to_orphan_it.emplace(tx->GetWitnessHash(), ret.first);
+ for (const CTxIn& txin : tx->vin) {
+ m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
+ }
+
+ LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
+ m_orphans.size(), m_outpoint_to_orphan_it.size());
+ return true;
+}
+
+int TxOrphanage::EraseTx(const uint256& txid)
+{
+ AssertLockHeld(g_cs_orphans);
+ std::map<uint256, OrphanTx>::iterator it = m_orphans.find(txid);
+ if (it == m_orphans.end())
+ return 0;
+ for (const CTxIn& txin : it->second.tx->vin)
+ {
+ auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
+ if (itPrev == m_outpoint_to_orphan_it.end())
+ continue;
+ itPrev->second.erase(it);
+ if (itPrev->second.empty())
+ m_outpoint_to_orphan_it.erase(itPrev);
+ }
+
+ size_t old_pos = it->second.list_pos;
+ assert(m_orphan_list[old_pos] == it);
+ if (old_pos + 1 != m_orphan_list.size()) {
+ // Unless we're deleting the last entry in m_orphan_list, move the last
+ // entry to the position we're deleting.
+ auto it_last = m_orphan_list.back();
+ m_orphan_list[old_pos] = it_last;
+ it_last->second.list_pos = old_pos;
+ }
+ m_orphan_list.pop_back();
+ m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
+
+ m_orphans.erase(it);
+ return 1;
+}
+
+void TxOrphanage::EraseForPeer(NodeId peer)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ int nErased = 0;
+ std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
+ while (iter != m_orphans.end())
+ {
+ std::map<uint256, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
+ if (maybeErase->second.fromPeer == peer)
+ {
+ nErased += EraseTx(maybeErase->second.tx->GetHash());
+ }
+ }
+ if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
+}
+
+unsigned int TxOrphanage::LimitOrphans(unsigned int max_orphans)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ unsigned int nEvicted = 0;
+ static int64_t nNextSweep;
+ int64_t nNow = GetTime();
+ if (nNextSweep <= nNow) {
+ // Sweep out expired orphan pool entries:
+ int nErased = 0;
+ int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
+ std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
+ while (iter != m_orphans.end())
+ {
+ std::map<uint256, OrphanTx>::iterator maybeErase = iter++;
+ if (maybeErase->second.nTimeExpire <= nNow) {
+ nErased += EraseTx(maybeErase->second.tx->GetHash());
+ } else {
+ nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
+ }
+ }
+ // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
+ nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
+ if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
+ }
+ FastRandomContext rng;
+ while (m_orphans.size() > max_orphans)
+ {
+ // Evict a random orphan:
+ size_t randompos = rng.randrange(m_orphan_list.size());
+ EraseTx(m_orphan_list[randompos]->first);
+ ++nEvicted;
+ }
+ return nEvicted;
+}
+
+void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const
+{
+ AssertLockHeld(g_cs_orphans);
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
+ if (it_by_prev != m_outpoint_to_orphan_it.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ orphan_work_set.insert(elem->first);
+ }
+ }
+ }
+}
+
+bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
+{
+ LOCK(g_cs_orphans);
+ if (gtxid.IsWtxid()) {
+ return m_wtxid_to_orphan_it.count(gtxid.GetHash());
+ } else {
+ return m_orphans.count(gtxid.GetHash());
+ }
+}
+
+std::pair<CTransactionRef, NodeId> TxOrphanage::GetTx(const uint256& txid) const
+{
+ AssertLockHeld(g_cs_orphans);
+
+ const auto it = m_orphans.find(txid);
+ if (it == m_orphans.end()) return {nullptr, -1};
+ return {it->second.tx, it->second.fromPeer};
+}
+
+void TxOrphanage::EraseForBlock(const CBlock& block)
+{
+ LOCK(g_cs_orphans);
+
+ std::vector<uint256> vOrphanErase;
+
+ for (const CTransactionRef& ptx : block.vtx) {
+ const CTransaction& tx = *ptx;
+
+ // Which orphan pool entries must we evict?
+ for (const auto& txin : tx.vin) {
+ auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
+ if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = *(*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
+ }
+ }
+
+ // Erase orphan transactions included or precluded by this block
+ if (vOrphanErase.size()) {
+ int nErased = 0;
+ for (const uint256& orphanHash : vOrphanErase) {
+ nErased += EraseTx(orphanHash);
+ }
+ LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
+ }
+}
diff --git a/src/txorphanage.h b/src/txorphanage.h
new file mode 100644
index 0000000000..df55cdb3be
--- /dev/null
+++ b/src/txorphanage.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TXORPHANAGE_H
+#define BITCOIN_TXORPHANAGE_H
+
+#include <net.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <sync.h>
+
+/** Guards orphan transactions and extra txs for compact blocks */
+extern RecursiveMutex g_cs_orphans;
+
+/** A class to track orphan transactions (failed on TX_MISSING_INPUTS)
+ * Since we cannot distinguish orphans from bad transactions with
+ * non-existent inputs, we heavily limit the number of orphans
+ * we keep and the duration we keep them for.
+ */
+class TxOrphanage {
+public:
+ /** Add a new orphan transaction */
+ bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Check if we already have an orphan transaction (by txid or wtxid) */
+ bool HaveTx(const GenTxid& gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!g_cs_orphans);
+
+ /** Get an orphan transaction and its orginating peer
+ * (Transaction ref will be nullptr if not found)
+ */
+ std::pair<CTransactionRef, NodeId> GetTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase an orphan by txid */
+ int EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase all orphans announced by a peer (eg, after that peer disconnects) */
+ void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase all orphans included in or invalidated by a new block */
+ void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(!g_cs_orphans);
+
+ /** Limit the orphanage to the given maximum */
+ unsigned int LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Add any orphans that list a particular tx as a parent into a peer's work set
+ * (ie orphans that may have found their final missing parent, and so should be reconsidered for the mempool) */
+ void AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+protected:
+ struct OrphanTx {
+ CTransactionRef tx;
+ NodeId fromPeer;
+ int64_t nTimeExpire;
+ size_t list_pos;
+ };
+
+ /** Map from txid to orphan transaction record. Limited by
+ * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
+ std::map<uint256, OrphanTx> m_orphans GUARDED_BY(g_cs_orphans);
+
+ using OrphanMap = decltype(m_orphans);
+
+ struct IteratorComparator
+ {
+ template<typename I>
+ bool operator()(const I& a, const I& b) const
+ {
+ return &(*a) < &(*b);
+ }
+ };
+
+ /** Index from the parents' COutPoint into the m_orphans. Used
+ * to remove orphan transactions from the m_orphans */
+ std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(g_cs_orphans);
+
+ /** Orphan transactions in vector for quick random eviction */
+ std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(g_cs_orphans);
+
+ /** Index from wtxid into the m_orphans to lookup orphan
+ * transactions using their witness ids. */
+ std::map<uint256, OrphanMap::iterator> m_wtxid_to_orphan_it GUARDED_BY(g_cs_orphans);
+};
+
+#endif // BITCOIN_TXORPHANAGE_H
diff --git a/src/util/epochguard.h b/src/util/epochguard.h
new file mode 100644
index 0000000000..1570ec4eb4
--- /dev/null
+++ b/src/util/epochguard.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_EPOCHGUARD_H
+#define BITCOIN_UTIL_EPOCHGUARD_H
+
+#include <threadsafety.h>
+
+#include <cassert>
+
+/** Epoch: RAII-style guard for using epoch-based graph traversal algorithms.
+ * When walking ancestors or descendants, we generally want to avoid
+ * visiting the same transactions twice. Some traversal algorithms use
+ * std::set (or setEntries) to deduplicate the transaction we visit.
+ * However, use of std::set is algorithmically undesirable because it both
+ * adds an asymptotic factor of O(log n) to traversals cost and triggers O(n)
+ * more dynamic memory allocations.
+ * In many algorithms we can replace std::set with an internal mempool
+ * counter to track the time (or, "epoch") that we began a traversal, and
+ * check + update a per-transaction epoch for each transaction we look at to
+ * determine if that transaction has not yet been visited during the current
+ * traversal's epoch.
+ * Algorithms using std::set can be replaced on a one by one basis.
+ * Both techniques are not fundamentally incompatible across the codebase.
+ * Generally speaking, however, the remaining use of std::set for mempool
+ * traversal should be viewed as a TODO for replacement with an epoch based
+ * traversal, rather than a preference for std::set over epochs in that
+ * algorithm.
+ */
+
+class LOCKABLE Epoch
+{
+private:
+ uint64_t m_raw_epoch = 0;
+ bool m_guarded = false;
+
+public:
+ Epoch() = default;
+ Epoch(const Epoch&) = delete;
+ Epoch& operator=(const Epoch&) = delete;
+
+ bool guarded() const { return m_guarded; }
+
+ class Marker
+ {
+ private:
+ uint64_t m_marker = 0;
+
+ // only allow modification via Epoch member functions
+ friend class Epoch;
+ Marker& operator=(const Marker&) = delete;
+ };
+
+ class SCOPED_LOCKABLE Guard
+ {
+ private:
+ Epoch& m_epoch;
+
+ public:
+ explicit Guard(Epoch& epoch) EXCLUSIVE_LOCK_FUNCTION(epoch) : m_epoch(epoch)
+ {
+ assert(!m_epoch.m_guarded);
+ ++m_epoch.m_raw_epoch;
+ m_epoch.m_guarded = true;
+ }
+ ~Guard() UNLOCK_FUNCTION()
+ {
+ assert(m_epoch.m_guarded);
+ ++m_epoch.m_raw_epoch; // ensure clear separation between epochs
+ m_epoch.m_guarded = false;
+ }
+ };
+
+ bool visited(Marker& marker) const EXCLUSIVE_LOCKS_REQUIRED(*this)
+ {
+ assert(m_guarded);
+ if (marker.m_marker < m_raw_epoch) {
+ // marker is from a previous epoch, so this is its first visit
+ marker.m_marker = m_raw_epoch;
+ return false;
+ } else {
+ return true;
+ }
+ }
+};
+
+#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard PASTE2(epoch_guard_, __COUNTER__)(epoch)
+
+#endif // BITCOIN_UTIL_EPOCHGUARD_H
diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp
index 1bc8d02eab..3f9ce7dce4 100644
--- a/src/util/moneystr.cpp
+++ b/src/util/moneystr.cpp
@@ -9,13 +9,17 @@
#include <util/strencodings.h>
#include <util/string.h>
-std::string FormatMoney(const CAmount& n)
+std::string FormatMoney(const CAmount n)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
- int64_t n_abs = (n > 0 ? n : -n);
- int64_t quotient = n_abs/COIN;
- int64_t remainder = n_abs%COIN;
+ static_assert(COIN > 1);
+ int64_t quotient = n / COIN;
+ int64_t remainder = n % COIN;
+ if (n < 0) {
+ quotient = -quotient;
+ remainder = -remainder;
+ }
std::string str = strprintf("%d.%08d", quotient, remainder);
// Right-trim excess zeros before the decimal point:
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index da7f673cda..2aedbee358 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -17,7 +17,7 @@
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
-std::string FormatMoney(const CAmount& n);
+std::string FormatMoney(const CAmount n);
/** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/
[[nodiscard]] bool ParseMoney(const std::string& str, CAmount& nRet);
diff --git a/src/util/readwritefile.cpp b/src/util/readwritefile.cpp
new file mode 100644
index 0000000000..a45c41d367
--- /dev/null
+++ b/src/util/readwritefile.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2015-2020 The Bitcoin Core developers
+// Copyright (c) 2017 The Zcash developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <fs.h>
+
+#include <limits>
+#include <stdio.h>
+#include <string>
+#include <utility>
+
+std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
+{
+ FILE *f = fsbridge::fopen(filename, "rb");
+ if (f == nullptr)
+ return std::make_pair(false,"");
+ std::string retval;
+ char buffer[128];
+ do {
+ const size_t n = fread(buffer, 1, sizeof(buffer), f);
+ // Check for reading errors so we don't return any data if we couldn't
+ // read the entire file (or up to maxsize)
+ if (ferror(f)) {
+ fclose(f);
+ return std::make_pair(false,"");
+ }
+ retval.append(buffer, buffer+n);
+ } while (!feof(f) && retval.size() <= maxsize);
+ fclose(f);
+ return std::make_pair(true,retval);
+}
+
+bool WriteBinaryFile(const fs::path &filename, const std::string &data)
+{
+ FILE *f = fsbridge::fopen(filename, "wb");
+ if (f == nullptr)
+ return false;
+ if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
+ fclose(f);
+ return false;
+ }
+ if (fclose(f) != 0) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/util/readwritefile.h b/src/util/readwritefile.h
new file mode 100644
index 0000000000..1dab874b38
--- /dev/null
+++ b/src/util/readwritefile.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_READWRITEFILE_H
+#define BITCOIN_UTIL_READWRITEFILE_H
+
+#include <fs.h>
+
+#include <limits>
+#include <string>
+#include <utility>
+
+/** Read full contents of a file and return them in a std::string.
+ * Returns a pair <status, string>.
+ * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
+ *
+ * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
+ * (with len > maxsize) will be returned.
+ */
+std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
+
+/** Write contents of std::string to a file.
+ * @return true on success.
+ */
+bool WriteBinaryFile(const fs::path &filename, const std::string &data);
+
+#endif /* BITCOIN_UTIL_READWRITEFILE_H */
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 4c65b5b680..e13c52a16a 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -4,6 +4,7 @@
#include <compat.h>
#include <logging.h>
+#include <threadinterrupt.h>
#include <tinyformat.h>
#include <util/sock.h>
#include <util/system.h>
@@ -12,12 +13,18 @@
#include <codecvt>
#include <cwchar>
#include <locale>
+#include <stdexcept>
#include <string>
#ifdef USE_POLL
#include <poll.h>
#endif
+static inline bool IOErrorIsPermanent(int err)
+{
+ return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
+}
+
Sock::Sock() : m_socket(INVALID_SOCKET) {}
Sock::Sock(SOCKET s) : m_socket(s) {}
@@ -59,7 +66,7 @@ ssize_t Sock::Recv(void* buf, size_t len, int flags) const
return recv(m_socket, static_cast<char*>(buf), len, flags);
}
-bool Sock::Wait(std::chrono::milliseconds timeout, Event requested) const
+bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
#ifdef USE_POLL
pollfd fd;
@@ -72,7 +79,21 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested) const
fd.events |= POLLOUT;
}
- return poll(&fd, 1, count_milliseconds(timeout)) != SOCKET_ERROR;
+ if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
+ return false;
+ }
+
+ if (occurred != nullptr) {
+ *occurred = 0;
+ if (fd.revents & POLLIN) {
+ *occurred |= RECV;
+ }
+ if (fd.revents & POLLOUT) {
+ *occurred |= SEND;
+ }
+ }
+
+ return true;
#else
if (!IsSelectableSocket(m_socket)) {
return false;
@@ -93,10 +114,167 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested) const
timeval timeout_struct = MillisToTimeval(timeout);
- return select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) != SOCKET_ERROR;
+ if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
+ return false;
+ }
+
+ if (occurred != nullptr) {
+ *occurred = 0;
+ if (FD_ISSET(m_socket, &fdset_recv)) {
+ *occurred |= RECV;
+ }
+ if (FD_ISSET(m_socket, &fdset_send)) {
+ *occurred |= SEND;
+ }
+ }
+
+ return true;
#endif /* USE_POLL */
}
+void Sock::SendComplete(const std::string& data,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const
+{
+ const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
+ size_t sent{0};
+
+ for (;;) {
+ const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
+
+ if (ret > 0) {
+ sent += static_cast<size_t>(ret);
+ if (sent == data.size()) {
+ break;
+ }
+ } else {
+ const int err{WSAGetLastError()};
+ if (IOErrorIsPermanent(err)) {
+ throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
+ }
+ }
+
+ const auto now = GetTime<std::chrono::milliseconds>();
+
+ if (now >= deadline) {
+ throw std::runtime_error(strprintf(
+ "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
+ }
+
+ if (interrupt) {
+ throw std::runtime_error(strprintf(
+ "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
+ }
+
+ // Wait for a short while (or the socket to become ready for sending) before retrying
+ // if nothing was sent.
+ const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ Wait(wait_time, SEND);
+ }
+}
+
+std::string Sock::RecvUntilTerminator(uint8_t terminator,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const
+{
+ const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
+ std::string data;
+ bool terminator_found{false};
+
+ // We must not consume any bytes past the terminator from the socket.
+ // One option is to read one byte at a time and check if we have read a terminator.
+ // However that is very slow. Instead, we peek at what is in the socket and only read
+ // as many bytes as possible without crossing the terminator.
+ // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
+ // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
+ // at a time is about 50 times slower.
+
+ for (;;) {
+ char buf[512];
+
+ const ssize_t peek_ret{Recv(buf, sizeof(buf), MSG_PEEK)};
+
+ switch (peek_ret) {
+ case -1: {
+ const int err{WSAGetLastError()};
+ if (IOErrorIsPermanent(err)) {
+ throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
+ }
+ break;
+ }
+ case 0:
+ throw std::runtime_error("Connection unexpectedly closed by peer");
+ default:
+ auto end = buf + peek_ret;
+ auto terminator_pos = std::find(buf, end, terminator);
+ terminator_found = terminator_pos != end;
+
+ const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
+ static_cast<size_t>(peek_ret)};
+
+ const ssize_t read_ret{Recv(buf, try_len, 0)};
+
+ if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
+ throw std::runtime_error(
+ strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
+ "peek claimed %u bytes are available",
+ read_ret, try_len, peek_ret));
+ }
+
+ // Don't include the terminator in the output.
+ const size_t append_len{terminator_found ? try_len - 1 : try_len};
+
+ data.append(buf, buf + append_len);
+
+ if (terminator_found) {
+ return data;
+ }
+ }
+
+ const auto now = GetTime<std::chrono::milliseconds>();
+
+ if (now >= deadline) {
+ throw std::runtime_error(strprintf(
+ "Receive timeout (received %u bytes without terminator before that)", data.size()));
+ }
+
+ if (interrupt) {
+ throw std::runtime_error(strprintf(
+ "Receive interrupted (received %u bytes without terminator before that)",
+ data.size()));
+ }
+
+ // Wait for a short while (or the socket to become ready for reading) before retrying.
+ const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ Wait(wait_time, RECV);
+ }
+}
+
+bool Sock::IsConnected(std::string& errmsg) const
+{
+ if (m_socket == INVALID_SOCKET) {
+ errmsg = "not connected";
+ return false;
+ }
+
+ char c;
+ switch (Recv(&c, sizeof(c), MSG_PEEK)) {
+ case -1: {
+ const int err = WSAGetLastError();
+ if (IOErrorIsPermanent(err)) {
+ errmsg = NetworkErrorString(err);
+ return false;
+ }
+ return true;
+ }
+ case 0:
+ errmsg = "closed";
+ return false;
+ default:
+ return true;
+ }
+}
+
#ifdef WIN32
std::string NetworkErrorString(int err)
{
diff --git a/src/util/sock.h b/src/util/sock.h
index 26fe60f18f..ecebb84205 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -6,11 +6,19 @@
#define BITCOIN_UTIL_SOCK_H
#include <compat.h>
+#include <threadinterrupt.h>
+#include <util/time.h>
#include <chrono>
#include <string>
/**
+ * Maximum time to wait for I/O readiness.
+ * It will take up until this time to break off in case of an interruption.
+ */
+static constexpr auto MAX_WAIT_FOR_IO = 1s;
+
+/**
* RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
* contains a socket and closes it automatically when it goes out of scope.
*/
@@ -98,9 +106,49 @@ public:
* Wait for readiness for input (recv) or output (send).
* @param[in] timeout Wait this much for at least one of the requested events to occur.
* @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
+ * @param[out] occurred If not nullptr and `true` is returned, then upon return this
+ * indicates which of the requested events occurred. A timeout is indicated by return
+ * value of `true` and `occurred` being set to 0.
* @return true on success and false otherwise
*/
- virtual bool Wait(std::chrono::milliseconds timeout, Event requested) const;
+ virtual bool Wait(std::chrono::milliseconds timeout,
+ Event requested,
+ Event* occurred = nullptr) const;
+
+ /* Higher level, convenience, methods. These may throw. */
+
+ /**
+ * Send the given data, retrying on transient errors.
+ * @param[in] data Data to send.
+ * @param[in] timeout Timeout for the entire operation.
+ * @param[in] interrupt If this is signaled then the operation is canceled.
+ * @throws std::runtime_error if the operation cannot be completed. In this case only some of
+ * the data will be written to the socket.
+ */
+ virtual void SendComplete(const std::string& data,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const;
+
+ /**
+ * Read from socket until a terminator character is encountered. Will never consume bytes past
+ * the terminator from the socket.
+ * @param[in] terminator Character up to which to read from the socket.
+ * @param[in] timeout Timeout for the entire operation.
+ * @param[in] interrupt If this is signaled then the operation is canceled.
+ * @return The data that has been read, without the terminating character.
+ * @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
+ * have been consumed from the socket.
+ */
+ virtual std::string RecvUntilTerminator(uint8_t terminator,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const;
+
+ /**
+ * Check if still connected.
+ * @param[out] err The error string, if the socket has been disconnected.
+ * @return true if connected
+ */
+ virtual bool IsConnected(std::string& errmsg) const;
private:
/**
diff --git a/src/util/time.h b/src/util/time.h
index 56131ce0fe..7ebcaaa339 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -26,9 +26,16 @@ void UninterruptibleSleep(const std::chrono::microseconds& n);
* This helper is used to convert durations before passing them over an
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
-inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
-inline int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
-inline int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
+constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
+constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
+constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
+
+using SecondsDouble = std::chrono::duration<double, std::chrono::seconds::period>;
+
+/**
+ * Helper to count the seconds in any std::chrono::duration type
+ */
+inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
/**
* DEPRECATED
diff --git a/src/validation.cpp b/src/validation.cpp
index 0b2ca4b422..0c3aeced93 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -211,6 +211,7 @@ static FlatFileSeq UndoFileSeq();
bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags)
{
AssertLockHeld(cs_main);
+ assert(active_chain_tip); // TODO: Make active_chain_tip a reference
assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
@@ -221,12 +222,12 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
// scheduled, so no flags are set.
flags = std::max(flags, 0);
- // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate
+ // CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
// CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
- // IsFinalTx() with one more than ::ChainActive().Height().
+ // IsFinalTx() with one more than active_chain_tip.Height().
const int nBlockHeight = active_chain_tip->nHeight + 1;
// BIP113 requires that time-locked transactions have nLockTime set to
@@ -1362,16 +1363,18 @@ static void AlertNotify(const std::string& strMessage)
#endif
}
-static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
- if (::ChainstateActive().IsInitialBlockDownload()) {
+ if (IsInitialBlockDownload()) {
return;
}
- if (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)) {
+ if (pindexBestInvalid && pindexBestInvalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) {
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
SetfLargeWorkInvalidChainFound(true);
} else {
@@ -1380,21 +1383,22 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
}
// Called both upon regular invalid block discovery *and* InvalidateBlock
-void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
- pindexBestHeader = ::ChainActive().Tip();
+ pindexBestHeader = m_chain.Tip();
}
LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
- CBlockIndex *tip = ::ChainActive().Tip();
+ CBlockIndex *tip = m_chain.Tip();
assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n", __func__,
- tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
+ tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
}
@@ -2432,7 +2436,7 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
@@ -2445,7 +2449,8 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- if (!::ChainstateActive().IsInitialBlockDownload()) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (!active_chainstate.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
@@ -2460,11 +2465,12 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
@@ -2520,7 +2526,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(m_mempool, pindexDelete->pprev, chainparams);
+ UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2628,7 +2634,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(m_mempool, pindexNew, chainparams);
+ UpdateTip(m_mempool, pindexNew, chainparams, *this);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2719,6 +2725,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2730,7 +2737,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2774,7 +2781,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
return false;
}
} else {
@@ -2791,9 +2798,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, true);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
}
- m_mempool.check(&CoinsTip());
+ m_mempool.check(*this);
CheckForkWarningConditions();
@@ -2964,9 +2971,6 @@ bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams&
return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
}
-bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
- return ::ChainstateActive().PreciousBlock(state, params, pindex);
-}
bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
@@ -3028,7 +3032,8 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3104,10 +3109,6 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
return true;
}
-bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
- return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
-}
-
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
@@ -3142,10 +3143,6 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
}
-void ResetBlockFailureFlags(CBlockIndex *pindex) {
- return ::ChainstateActive().ResetBlockFailureFlags(pindex);
-}
-
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{
AssertLockHeld(cs_main);
@@ -3229,7 +3226,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
}
}
-static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -3244,7 +3241,8 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
+ assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -3706,12 +3704,12 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) {
+static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) {
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
FlatFilePos blockPos;
if (dbp != nullptr)
blockPos = *dbp;
- if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
+ if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) {
error("%s: FindBlockPos failed", __func__);
return FlatFilePos();
}
@@ -3789,8 +3787,9 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
+ assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
- FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+ FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3961,11 +3960,12 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM
}
/* This function is called from the RPC code for pruneblockchain */
-void PruneBlockFilesManual(int nManualPruneHeight)
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
{
BlockValidationState state;
const CChainParams& chainparams = Params();
- if (!::ChainstateActive().FlushStateToDisk(
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (!active_chainstate.FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
@@ -4140,11 +4140,12 @@ void BlockManager::Unload() {
m_block_index.clear();
}
-bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
{
- if (!chainman.m_blockman.LoadBlockIndex(
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ if (!m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
- ::ChainstateActive().setBlockIndexCandidates)) {
+ setBlockIndexCandidates)) {
return false;
}
@@ -4168,7 +4169,7 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_blockman.m_block_index) {
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
@@ -4242,15 +4243,17 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100, false);
}
-bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
- LOCK(cs_main);
- if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr)
+ AssertLockHeld(cs_main);
+
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (active_chainstate.m_chain.Tip() == nullptr || active_chainstate.m_chain.Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height())
- nCheckDepth = ::ChainActive().Height();
+ if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height())
+ nCheckDepth = active_chainstate.m_chain.Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
@@ -4260,15 +4263,15 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
BlockValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
- const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+ for (pindex = active_chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
+ if (pindex->nHeight <= active_chainstate.m_chain.Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -4293,9 +4296,9 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= ::ChainstateActive().m_coinstip_cache_size_bytes) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4309,26 +4312,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (ShutdownRequested()) return true;
}
if (pindexFailure)
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", active_chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = ::ChainActive().Height() - pindex->nHeight;
+ int block_count = active_chainstate.m_chain.Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- while (pindex != ::ChainActive().Tip()) {
- const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ while (pindex != active_chainstate.m_chain.Tip()) {
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- pindex = ::ChainActive().Next(pindex);
+ pindex = active_chainstate.m_chain.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
if (ShutdownRequested()) return true;
}
@@ -4599,7 +4602,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = LoadBlockIndexDB(*this, chainparams);
+ bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams);
if (!ret) return false;
needs_init = m_blockman.m_block_index.empty();
}
@@ -4627,9 +4630,10 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
+ assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
const CBlock& block = chainparams.GenesisBlock();
- FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+ FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
@@ -4641,11 +4645,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
return true;
}
-bool LoadGenesisBlock(const CChainParams& chainparams)
-{
- return ::ChainstateActive().LoadGenesisBlock(chainparams);
-}
-
void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
@@ -5009,24 +5008,6 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
return &vinfoBlockFile.at(n);
}
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsStatistics(::ChainActive().Tip(), params, pos);
-}
-
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
@@ -5444,7 +5425,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
+ if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
diff --git a/src/validation.h b/src/validation.h
index 4e4bdbea54..512b306219 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -148,8 +148,6 @@ extern const std::vector<std::string> CHECKLEVEL_DOC;
FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
fs::path GetBlockPosFilename(const FlatFilePos &pos);
-/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
-bool LoadGenesisBlock(const CChainParams& chainparams);
/** Unload database information */
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
/** Run instances of script checking worker threads */
@@ -183,7 +181,7 @@ uint64_t CalculateCurrentUsage();
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Prune block files up to a given height */
-void PruneBlockFilesManual(int nManualPruneHeight);
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
/**
* Validation result for a single transaction mempool acceptance.
@@ -226,15 +224,6 @@ struct MempoolAcceptResult {
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Get the BIP9 state for a given deployment at the current tip. */
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
-/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
-/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
@@ -349,7 +338,7 @@ class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
- bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
+ bool VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
enum DisconnectResult
@@ -721,13 +710,20 @@ public:
bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
+ /** Mark a block as precious and reorganize.
+ *
+ * May not be called in a validationinterface callback.
+ */
bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ /** Mark a block as invalid. */
bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ /** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params);
bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
+ /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
void PruneBlockIndexCandidates();
@@ -776,21 +772,13 @@ private:
//! Mark a block as not having block data
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- friend ChainstateManager;
-};
-
-/** Mark a block as precious and reorganize.
- *
- * May not be called in a
- * validationinterface callback.
- */
-bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
+ void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Mark a block as invalid. */
-bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Remove invalidity status from a block and its descendants. */
-void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ friend ChainstateManager;
+};
/**
* Provides an interface for creating and interacting with one or two
diff --git a/src/versionbits.h b/src/versionbits.h
index b02f848b67..6df1db8814 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -79,8 +79,11 @@ struct VersionBitsCache
void Clear();
};
+/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 5e319d4f95..08adf09df4 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -190,7 +190,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
// We calculate this here to avoid compiler warning on the cs_wallet lock
- const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet);
+ const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet).first;
Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors);
if (res != Result::OK) {
return res;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index fc530ee286..f3e24384df 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -62,7 +62,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#ifdef ENABLE_EXTERNAL_SIGNER
- argsman.AddArg("-signer=<cmd>", "External signing tool, see docs/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 99803a91d2..60f76465a6 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -56,13 +56,13 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static bool GetWalletAddressesForKey(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;
spk_man->GetKey(keyid, key);
for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
- const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(dest);
if (address_book_entry) {
if (!strAddr.empty()) {
strAddr += ",";
@@ -73,7 +73,7 @@ static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWall
}
}
if (!fLabelFound) {
- strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), pwallet->m_default_address_type));
+ strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), wallet.m_default_address_type));
}
return fLabelFound;
}
@@ -118,22 +118,21 @@ RPCHelpMan importprivkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
WalletRescanReserver reserver(*pwallet);
bool fRescan = true;
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strSecret = request.params[0].get_str();
std::string strLabel = "";
@@ -210,9 +209,8 @@ RPCHelpMan abortrescan()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
@@ -249,9 +247,8 @@ RPCHelpMan importaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -335,9 +332,8 @@ RPCHelpMan importprunedfunds()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
@@ -397,9 +393,8 @@ RPCHelpMan removeprunedfunds()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -445,11 +440,10 @@ RPCHelpMan importpubkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel;
if (!request.params[1].isNull())
@@ -527,11 +521,10 @@ RPCHelpMan importwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->chain().havePruned()) {
// Exit early and print an error.
@@ -550,7 +543,7 @@ RPCHelpMan importwallet()
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file;
file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
@@ -684,15 +677,14 @@ RPCHelpMan dumpprivkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str();
CTxDestination dest = DecodeDestination(strAddress);
@@ -747,7 +739,7 @@ RPCHelpMan dumpwallet()
LOCK2(wallet.cs_wallet, spk_man.cs_KeyStore);
- EnsureWalletIsUnlocked(&wallet);
+ EnsureWalletIsUnlocked(wallet);
fs::path filepath = request.params[0].get_str();
filepath = fs::absolute(filepath);
@@ -809,7 +801,7 @@ RPCHelpMan dumpwallet()
CKey key;
if (spk_man.GetKey(keyid, key)) {
file << strprintf("%s %s ", EncodeSecret(key), strTime);
- if (GetWalletAddressesForKey(&spk_man, &wallet, keyid, strAddr, strLabel)) {
+ if (GetWalletAddressesForKey(&spk_man, wallet, keyid, strAddr, strLabel)) {
file << strprintf("label=%s", strLabel);
} else if (keyid == seed_id) {
file << "hdseed=1";
@@ -1169,7 +1161,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
return warnings;
}
-static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ);
@@ -1184,7 +1176,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
// Add to keypool only works with privkeys disabled
- if (add_keypool && !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (add_keypool && !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
}
@@ -1206,29 +1198,29 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
}
// If private keys are disabled, abort if private keys are being imported
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
// Check whether we have any work to do
for (const CScript& script : script_pub_keys) {
- if (pwallet->IsMine(script) & ISMINE_SPENDABLE) {
+ if (wallet.IsMine(script) & ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
}
}
// All good, time to import
- pwallet->MarkDirty();
- if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) {
+ wallet.MarkDirty();
+ if (!wallet.ImportScripts(import_data.import_scripts, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
- if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
+ if (!wallet.ImportPrivKeys(privkey_map, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
+ if (!wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
+ if (!wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
@@ -1336,13 +1328,12 @@ RPCHelpMan importmulti()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
const UniValue& requests = mainRequest.params[0];
@@ -1368,7 +1359,7 @@ RPCHelpMan importmulti()
UniValue response(UniValue::VARR);
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
// Verify all timestamps are present before importing any keys.
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
@@ -1380,7 +1371,7 @@ RPCHelpMan importmulti()
for (const UniValue& data : requests.getValues()) {
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
- const UniValue result = ProcessImport(pwallet, data, timestamp);
+ const UniValue result = ProcessImport(*pwallet, data, timestamp);
response.push_back(result);
if (!fRescan) {
@@ -1447,7 +1438,7 @@ RPCHelpMan importmulti()
};
}
-static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ);
@@ -1516,7 +1507,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// If the wallet disabled private keys, abort if private keys exist
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@@ -1540,7 +1531,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// If private keys are enabled, check some things.
- if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
}
@@ -1552,7 +1543,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
// Check if the wallet already contains the descriptor
- auto existing_spk_manager = pwallet->GetDescriptorScriptPubKeyMan(w_desc);
+ auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
if (existing_spk_manager) {
LOCK(existing_spk_manager->cs_desc_man);
if (range_start > existing_spk_manager->GetWalletDescriptor().range_start) {
@@ -1561,7 +1552,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// Add descriptor to the wallet
- auto spk_manager = pwallet->AddWalletDescriptor(w_desc, keys, label, internal);
+ auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
if (spk_manager == nullptr) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
}
@@ -1571,7 +1562,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else {
- pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
+ wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
}
}
@@ -1641,9 +1632,8 @@ RPCHelpMan importdescriptors()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
+ if (!pwallet) return NullUniValue;
// Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
@@ -1665,7 +1655,7 @@ RPCHelpMan importdescriptors()
UniValue response(UniValue::VARR);
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
@@ -1673,7 +1663,7 @@ RPCHelpMan importdescriptors()
for (const UniValue& request : requests.getValues()) {
// This throws an error if "timestamp" doesn't exist
const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
- const UniValue result = ProcessDescriptorImport(pwallet, request, timestamp);
+ const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
response.push_back(result);
if (lowest_timestamp > timestamp ) {
@@ -1775,6 +1765,8 @@ RPCHelpMan listdescriptors()
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
}
+ EnsureWalletIsUnlocked(*wallet);
+
LOCK(wallet->cs_wallet);
UniValue response(UniValue::VARR);
@@ -1787,7 +1779,11 @@ RPCHelpMan listdescriptors()
UniValue spk(UniValue::VOBJ);
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
- spk.pushKV("desc", wallet_descriptor.descriptor->ToString());
+ std::string descriptor;
+ if (!desc_spk_man->GetDescriptorString(descriptor, false)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string.");
+ }
+ spk.pushKV("desc", descriptor);
spk.pushKV("timestamp", wallet_descriptor.creation_time);
const bool active = active_spk_mans.count(desc_spk_man) != 0;
spk.pushKV("active", active);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index bfc42ac1b0..8b0962f9ee 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -48,8 +48,8 @@ using interfaces::FoundBlock;
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
-static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
- bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+static inline bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
+ bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
if (avoid_reuse && !can_avoid_reuse) {
@@ -64,11 +64,11 @@ static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValu
* We default to true for watchonly wallets if include_watchonly isn't
* explicitly set.
*/
-static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet)
+static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
{
if (include_watchonly.isNull()) {
// if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
- return pwallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
}
// otherwise return whatever include_watchonly was set to
@@ -117,9 +117,9 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
}
-void EnsureWalletIsUnlocked(const CWallet* pwallet)
+void EnsureWalletIsUnlocked(const CWallet& wallet)
{
- if (pwallet->IsLocked()) {
+ if (wallet.IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
}
}
@@ -249,9 +249,8 @@ static RPCHelpMan getnewaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -299,9 +298,8 @@ static RPCHelpMan getrawchangeaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -342,9 +340,8 @@ static RPCHelpMan setlabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -396,13 +393,13 @@ void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_f
}
}
-UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
+UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
{
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(wallet);
// This function is only used by sendtoaddress and sendmany.
// This should always try to sign, if we don't have private keys, don't try to do anything here.
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
@@ -415,11 +412,11 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
bilingual_str error;
CTransactionRef tx;
FeeCalculation fee_calc_out;
- const bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
+ const bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
- pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
if (verbose) {
UniValue entry(UniValue::VOBJ);
entry.pushKV("txid", tx->GetHash().GetHex());
@@ -480,9 +477,8 @@ static RPCHelpMan sendtoaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -507,13 +503,13 @@ static RPCHelpMan sendtoaddress()
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[8]);
+ coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[8]);
// We also enable partial spend avoidance if reuse avoidance is set.
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
UniValue address_amounts(UniValue::VOBJ);
const std::string address = request.params[0].get_str();
@@ -527,7 +523,7 @@ static RPCHelpMan sendtoaddress()
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
const bool verbose{request.params[10].isNull() ? false : request.params[10].get_bool()};
- return SendMoney(pwallet, coin_control, recipients, mapValue, verbose);
+ return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
},
};
}
@@ -559,9 +555,8 @@ static RPCHelpMan listaddressgroupings()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -617,20 +612,19 @@ static RPCHelpMan signmessage()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CTxDestination dest = DecodeDestination(strAddress);
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
const PKHash* pkhash = std::get_if<PKHash>(&dest);
@@ -720,9 +714,8 @@ static RPCHelpMan getreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -759,9 +752,8 @@ static RPCHelpMan getreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -800,9 +792,8 @@ static RPCHelpMan getbalance()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -822,7 +813,7 @@ static RPCHelpMan getbalance()
bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
- bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
+ bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
@@ -840,9 +831,8 @@ static RPCHelpMan getunconfirmedbalance()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -911,9 +901,8 @@ static RPCHelpMan sendmany()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -945,7 +934,7 @@ static RPCHelpMan sendmany()
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
const bool verbose{request.params[9].isNull() ? false : request.params[9].get_bool()};
- return SendMoney(pwallet, coin_control, recipients, std::move(mapValue), verbose);
+ return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
},
};
}
@@ -985,9 +974,8 @@ static RPCHelpMan addmultisigaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
@@ -1045,7 +1033,7 @@ struct tallyitem
}
};
-static UniValue ListReceived(const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1059,7 +1047,7 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
isminefilter filter = ISMINE_SPENDABLE;
- if (ParseIncludeWatchonly(params[2], *pwallet)) {
+ if (ParseIncludeWatchonly(params[2], wallet)) {
filter |= ISMINE_WATCH_ONLY;
}
@@ -1075,10 +1063,10 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
// Tally
std::map<CTxDestination, tallyitem> mapTally;
- for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
+ for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !pwallet->chain().checkFinalTx(*wtx.tx)) {
+ if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx)) {
continue;
}
@@ -1096,7 +1084,7 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
continue;
}
- isminefilter mine = pwallet->IsMine(address);
+ isminefilter mine = wallet.IsMine(address);
if(!(mine & filter))
continue;
@@ -1115,11 +1103,11 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
// Create m_address_book iterator
// If we aren't filtering, go from begin() to end()
- auto start = pwallet->m_address_book.begin();
- auto end = pwallet->m_address_book.end();
+ auto start = wallet.m_address_book.begin();
+ auto end = wallet.m_address_book.end();
// If we are filtering, find() the applicable entry
if (has_filtered_address) {
- start = pwallet->m_address_book.find(filtered_address);
+ start = wallet.m_address_book.find(filtered_address);
if (start != end) {
end = std::next(start);
}
@@ -1227,9 +1215,8 @@ static RPCHelpMan listreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1237,7 +1224,7 @@ static RPCHelpMan listreceivedbyaddress()
LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, false);
+ return ListReceived(*pwallet, request.params, false);
},
};
}
@@ -1270,9 +1257,8 @@ static RPCHelpMan listreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1280,7 +1266,7 @@ static RPCHelpMan listreceivedbylabel()
LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, true);
+ return ListReceived(*pwallet, request.params, true);
},
};
}
@@ -1303,7 +1289,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1319,20 +1305,20 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& s : listSent)
{
UniValue entry(UniValue::VOBJ);
- if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
+ if (involvesWatchonly || (wallet.IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
- const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
if (address_book_entry) {
entry.pushKV("label", address_book_entry->GetLabel());
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(pwallet->chain(), wtx, entry);
+ WalletTxToJSON(wallet.chain(), wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
@@ -1343,7 +1329,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& r : listReceived)
{
std::string label;
- const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(r.destination);
if (address_book_entry) {
label = address_book_entry->GetLabel();
}
@@ -1351,7 +1337,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
continue;
}
UniValue entry(UniValue::VOBJ);
- if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
+ if (involvesWatchonly || (wallet.IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
MaybePushAddress(entry, r.destination);
@@ -1374,7 +1360,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(pwallet->chain(), wtx, entry);
+ WalletTxToJSON(wallet.chain(), wtx, entry);
ret.push_back(entry);
}
}
@@ -1451,9 +1437,8 @@ static RPCHelpMan listtransactions()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1494,7 +1479,7 @@ static RPCHelpMan listtransactions()
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(pwallet, *pwtx, 0, true, ret, filter, filter_label);
+ ListTransactions(*pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1616,7 +1601,7 @@ static RPCHelpMan listsinceblock()
const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
- ListTransactions(&wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1633,7 +1618,7 @@ static RPCHelpMan listsinceblock()
if (it != wallet.mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(&wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
}
}
blockId = block.hashPrevBlock;
@@ -1710,9 +1695,8 @@ static RPCHelpMan gettransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1749,7 +1733,7 @@ static RPCHelpMan gettransaction()
WalletTxToJSON(pwallet->chain(), wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
+ ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
@@ -1784,9 +1768,8 @@ static RPCHelpMan abandontransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1823,9 +1806,8 @@ static RPCHelpMan backupwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -1859,9 +1841,8 @@ static RPCHelpMan keypoolrefill()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
@@ -1877,7 +1858,7 @@ static RPCHelpMan keypoolrefill()
kpSize = (unsigned int)request.params[0].get_int();
}
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
pwallet->TopUpKeyPool(kpSize);
if (pwallet->GetKeyPoolSize() < kpSize) {
@@ -2001,9 +1982,8 @@ static RPCHelpMan walletpassphrasechange()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2055,9 +2035,8 @@ static RPCHelpMan walletlock()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2100,9 +2079,8 @@ static RPCHelpMan encryptwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2174,9 +2152,8 @@ static RPCHelpMan lockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -2289,9 +2266,8 @@ static RPCHelpMan listlockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2330,9 +2306,8 @@ static RPCHelpMan settxfee()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2460,9 +2435,8 @@ static RPCHelpMan getwalletinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// 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
@@ -2672,9 +2646,8 @@ static RPCHelpMan setwalletflag()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2916,9 +2889,8 @@ static RPCHelpMan listunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
int nMinDepth = 1;
if (!request.params[0].isNull()) {
@@ -3080,11 +3052,11 @@ static RPCHelpMan listunspent()
};
}
-void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
+void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
{
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
+ wallet.BlockUntilSyncedToCurrentChain();
change_position = -1;
bool lockUnspents = false;
@@ -3155,7 +3127,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, *pwallet);
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
@@ -3181,11 +3153,11 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- SetFeeEstimateMode(*pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
+ SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
}
} else {
// if options is null and not a bool
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet);
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
}
if (tx.vout.size() == 0)
@@ -3207,7 +3179,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
bilingual_str error;
- if (!pwallet->FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
}
@@ -3283,9 +3255,8 @@ static RPCHelpMan fundrawtransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
@@ -3302,7 +3273,7 @@ static RPCHelpMan fundrawtransaction()
CCoinControl coin_control;
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
coin_control.m_add_inputs = true;
- FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
+ FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
@@ -3369,9 +3340,8 @@ RPCHelpMan signrawtransactionwithwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
@@ -3382,7 +3352,7 @@ RPCHelpMan signrawtransactionwithwallet()
// Sign the transaction
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
// Fetch previous transactions (inputs):
std::map<COutPoint, Coin> coins;
@@ -3469,9 +3439,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
},
[want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) mutable -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
@@ -3514,7 +3483,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
pwallet->BlockUntilSyncedToCurrentChain();
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+
+ EnsureWalletIsUnlocked(*pwallet);
std::vector<bilingual_str> errors;
@@ -3608,9 +3578,8 @@ static RPCHelpMan rescanblockchain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) {
@@ -3759,15 +3728,13 @@ public:
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
-static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDestination& dest)
+static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
CScript script = GetScriptForDestination(dest);
std::unique_ptr<SigningProvider> provider = nullptr;
- if (pwallet) {
- provider = pwallet->GetSolvingProvider(script);
- }
+ provider = wallet.GetSolvingProvider(script);
ret.pushKVs(detail);
ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
return ret;
@@ -3840,9 +3807,8 @@ RPCHelpMan getaddressinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -3887,7 +3853,7 @@ RPCHelpMan getaddressinfo()
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
- UniValue detail = DescribeWalletAddress(pwallet, dest);
+ UniValue detail = DescribeWalletAddress(*pwallet, dest);
ret.pushKVs(detail);
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3943,9 +3909,8 @@ static RPCHelpMan getaddressesbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4005,9 +3970,8 @@ static RPCHelpMan listlabels()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4130,9 +4094,8 @@ static RPCHelpMan send()
}, true
);
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
if (options.exists("conf_target") || options.exists("estimate_mode")) {
@@ -4185,7 +4148,7 @@ static RPCHelpMan send()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
+ FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
bool add_to_wallet = true;
if (options.exists("add_to_wallet")) {
@@ -4258,9 +4221,8 @@ static RPCHelpMan sethdseed()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -4275,7 +4237,7 @@ static RPCHelpMan sethdseed()
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
}
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
bool flush_key_pool = true;
if (!request.params[0].isNull()) {
@@ -4336,9 +4298,8 @@ static RPCHelpMan walletprocesspsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
@@ -4448,9 +4409,8 @@ static RPCHelpMan walletcreatefundedpsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -4474,7 +4434,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
+ FundTransaction(*pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
@@ -4524,13 +4484,12 @@ static RPCHelpMan upgradewallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
int version = 0;
if (!request.params[0].isNull()) {
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 184a16e91d..b82fe1ec76 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -30,7 +30,7 @@ Span<const CRPCCommand> GetWalletRPCCommands();
*/
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
-void EnsureWalletIsUnlocked(const CWallet*);
+void EnsureWalletIsUnlocked(const CWallet&);
WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 08e480225d..58ab124061 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -791,6 +791,12 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
+ // Refresh mempool status without waiting for transactionRemovedFromMempool
+ // notification so the wallet is in an internally consistent state and
+ // immediately knows the old transaction should not be considered trusted
+ // and is eligible to be abandoned
+ wtx.fInMempool = chain().isInMempool(originalHash);
+
WalletBatch batch(GetDatabase());
bool success = true;
@@ -1611,14 +1617,15 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
return true;
}
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
+// Returns pair of vsize and weight
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
for (const CTxIn& input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.hash);
// Can not estimate size without knowing the input details
if (mi == wallet->mapWallet.end()) {
- return -1;
+ return std::make_pair(-1, -1);
}
assert(input.prevout.n < mi->second.tx->vout.size());
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
@@ -1627,13 +1634,16 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
}
// txouts needs to be in the order of tx.vin
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
{
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
- return -1;
+ return std::make_pair(-1, -1);
}
- return GetVirtualTransactionSize(CTransaction(txNew));
+ CTransaction ctx(txNew);
+ int64_t vsize = GetVirtualTransactionSize(ctx);
+ int64_t weight = GetTransactionWeight(ctx);
+ return std::make_pair(vsize, weight);
}
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
@@ -2779,6 +2789,7 @@ bool CWallet::CreateTransactionInternal(
CMutableTransaction txNew;
FeeCalculation feeCalc;
CAmount nFeeNeeded;
+ std::pair<int64_t, int64_t> tx_sizes;
int nBytes;
{
std::set<CInputCoin> setCoins;
@@ -2962,7 +2973,8 @@ bool CWallet::CreateTransactionInternal(
txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
}
- nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.first;
if (nBytes < 0) {
error = _("Signing transaction failed");
return false;
@@ -3072,7 +3084,8 @@ bool CWallet::CreateTransactionInternal(
tx = MakeTransactionRef(std::move(txNew));
// Limit size
- if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT))
{
error = _("Transaction too large");
return false;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index eb797938cd..3a76163dd2 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1334,8 +1334,8 @@ public:
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
//! Add wallet name to persistent configuration so it will be loaded on startup.
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);