aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/Makefile.test_util.include1
-rw-r--r--src/addrdb.cpp2
-rw-r--r--src/addrman.cpp154
-rw-r--r--src/addrman.h7
-rw-r--r--src/addrman_impl.h12
-rw-r--r--src/banman.cpp1
-rw-r--r--src/bench/addrman.cpp52
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bench/checkblock.cpp2
-rw-r--r--src/bench/coin_selection.cpp3
-rw-r--r--src/bench/pool.cpp50
-rw-r--r--src/bitcoin-chainstate.cpp2
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/bitcoin-tx.cpp1
-rw-r--r--src/bitcoin-util.cpp1
-rw-r--r--src/bitcoin-wallet.cpp3
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/chainparams.cpp3
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/chainparamsseeds.h1150
-rw-r--r--src/coins.cpp15
-rw-r--r--src/coins.h20
-rw-r--r--src/common/args.cpp823
-rw-r--r--src/common/args.h465
-rw-r--r--src/common/config.cpp220
-rw-r--r--src/common/init.cpp2
-rw-r--r--src/dummywallet.cpp3
-rw-r--r--src/httprpc.cpp3
-rw-r--r--src/httpserver.cpp6
-rw-r--r--src/i2p.cpp2
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/index/blockfilterindex.cpp2
-rw-r--r--src/index/coinstatsindex.cpp2
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp1
-rw-r--r--src/init/bitcoin-gui.cpp2
-rw-r--r--src/init/bitcoin-node.cpp2
-rw-r--r--src/init/bitcoin-qt.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/init/common.cpp2
-rw-r--r--src/kernel/chainparams.cpp42
-rw-r--r--src/memusage.h20
-rw-r--r--src/net.cpp9
-rw-r--r--src/net.h1
-rw-r--r--src/net_processing.cpp11
-rw-r--r--src/node/blockmanager_args.cpp2
-rw-r--r--src/node/blockstorage.cpp3
-rw-r--r--src/node/caches.cpp2
-rw-r--r--src/node/chainstatemanager_args.cpp2
-rw-r--r--src/node/coins_view_args.cpp2
-rw-r--r--src/node/database_args.cpp2
-rw-r--r--src/node/interfaces.cpp2
-rw-r--r--src/node/mempool_args.cpp2
-rw-r--r--src/node/mempool_persist_args.cpp2
-rw-r--r--src/node/miner.cpp2
-rw-r--r--src/node/txreconciliation.cpp1
-rw-r--r--src/node/utxo_snapshot.cpp2
-rw-r--r--src/node/validation_cache_args.cpp2
-rw-r--r--src/outputtype.cpp2
-rw-r--r--src/policy/fees_args.cpp2
-rw-r--r--src/protocol.cpp2
-rw-r--r--src/psbt.cpp32
-rw-r--r--src/psbt.h3
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/qt/guiutil.cpp3
-rw-r--r--src/qt/intro.cpp2
-rw-r--r--src/qt/optionsmodel.cpp6
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/test/optiontests.cpp2
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/rest.cpp26
-rw-r--r--src/rpc/blockchain.cpp2
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/external_signer.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp145
-rw-r--r--src/rpc/request.cpp3
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/server_util.cpp1
-rw-r--r--src/rpc/util.cpp2
-rw-r--r--src/script/descriptor.cpp2
-rw-r--r--src/script/sigcache.cpp1
-rw-r--r--src/signet.cpp5
-rw-r--r--src/support/allocators/pool.h349
-rw-r--r--src/test/addrman_tests.cpp126
-rw-r--r--src/test/argsman_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp2
-rw-r--r--src/test/coins_tests.cpp39
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/flatfile_tests.cpp2
-rw-r--r--src/test/fuzz/addrman.cpp2
-rw-r--r--src/test/fuzz/banman.cpp2
-rw-r--r--src/test/fuzz/coins_view.cpp3
-rw-r--r--src/test/fuzz/connman.cpp2
-rw-r--r--src/test/fuzz/deserialize.cpp2
-rw-r--r--src/test/fuzz/i2p.cpp2
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/poolresource.cpp174
-rw-r--r--src/test/fuzz/rpc.cpp8
-rw-r--r--src/test/fuzz/string.cpp1
-rw-r--r--src/test/fuzz/system.cpp2
-rw-r--r--src/test/fuzz/versionbits.cpp2
-rw-r--r--src/test/getarg_tests.cpp3
-rw-r--r--src/test/httpserver_tests.cpp4
-rw-r--r--src/test/i2p_tests.cpp2
-rw-r--r--src/test/net_tests.cpp2
-rw-r--r--src/test/pool_tests.cpp190
-rw-r--r--src/test/random_tests.cpp1
-rw-r--r--src/test/settings_tests.cpp2
-rw-r--r--src/test/txpackage_tests.cpp72
-rw-r--r--src/test/util/poolresourcetester.h129
-rw-r--r--src/test/util/setup_common.cpp28
-rw-r--r--src/test/util/setup_common.h14
-rw-r--r--src/test/validation_flush_tests.cpp33
-rw-r--r--src/test/validation_tests.cpp2
-rw-r--r--src/test/validationinterface_tests.cpp2
-rw-r--r--src/timedata.cpp3
-rw-r--r--src/torcontrol.cpp3
-rw-r--r--src/txmempool.cpp1
-rw-r--r--src/util/system.cpp1018
-rw-r--r--src/util/system.h444
-rw-r--r--src/validation.cpp18
-rw-r--r--src/wallet/bdb.cpp28
-rw-r--r--src/wallet/bdb.h6
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coinselection.cpp82
-rw-r--r--src/wallet/coinselection.h15
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/db.h2
-rw-r--r--src/wallet/dump.cpp2
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp2
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/interfaces.cpp17
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/rpc/addresses.cpp3
-rw-r--r--src/wallet/rpc/backup.cpp20
-rw-r--r--src/wallet/rpc/transactions.cpp10
-rw-r--r--src/wallet/rpc/wallet.cpp4
-rw-r--r--src/wallet/spend.cpp47
-rw-r--r--src/wallet/sqlite.cpp24
-rw-r--r--src/wallet/sqlite.h3
-rw-r--r--src/wallet/test/coinselector_tests.cpp234
-rw-r--r--src/wallet/test/fuzz/coinselection.cpp14
-rw-r--r--src/wallet/test/init_test_fixture.cpp2
-rw-r--r--src/wallet/test/init_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp69
-rw-r--r--src/wallet/wallet.cpp118
-rw-r--r--src/wallet/wallet.h36
-rw-r--r--src/wallet/walletdb.cpp35
-rw-r--r--src/wallet/walletdb.h9
-rw-r--r--src/wallet/wallettool.cpp2
-rw-r--r--src/wallet/walletutil.cpp2
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
157 files changed, 4324 insertions, 2578 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1d7004ac86..d12edca64e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -133,6 +133,7 @@ BITCOIN_CORE_H = \
checkqueue.h \
clientversion.h \
coins.h \
+ common/args.h \
common/bloom.h \
common/init.h \
common/run_command.h \
@@ -260,6 +261,7 @@ BITCOIN_CORE_H = \
shutdown.h \
signet.h \
streams.h \
+ support/allocators/pool.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
@@ -646,7 +648,9 @@ libbitcoin_common_a_SOURCES = \
bech32.cpp \
chainparams.cpp \
coins.cpp \
+ common/args.cpp \
common/bloom.cpp \
+ common/config.cpp \
common/init.cpp \
common/interfaces.cpp \
common/run_command.cpp \
@@ -901,6 +905,8 @@ libbitcoinkernel_la_SOURCES = \
chainparams.cpp \
clientversion.cpp \
coins.cpp \
+ common/args.cpp \
+ common/config.cpp \
compressor.cpp \
consensus/merkle.cpp \
consensus/tx_check.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index f1e4e706a1..c230728a1c 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -42,6 +42,7 @@ bench_bench_bitcoin_SOURCES = \
bench/nanobench.h \
bench/peer_eviction.cpp \
bench/poly1305.cpp \
+ bench/pool.cpp \
bench/prevector.cpp \
bench/rollingbloom.cpp \
bench/rpc_blockchain.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 15d5a17cec..69965ed1b8 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -116,6 +116,7 @@ BITCOIN_TESTS =\
test/pmt_tests.cpp \
test/policy_fee_tests.cpp \
test/policyestimator_tests.cpp \
+ test/pool_tests.cpp \
test/pow_tests.cpp \
test/prevector_tests.cpp \
test/raii_event_tests.cpp \
@@ -301,6 +302,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/partially_downloaded_block.cpp \
test/fuzz/policy_estimator.cpp \
test/fuzz/policy_estimator_io.cpp \
+ test/fuzz/poolresource.cpp \
test/fuzz/pow.cpp \
test/fuzz/prevector.cpp \
test/fuzz/primitives_transaction.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index aefefe789a..11b93ad13e 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -15,6 +15,7 @@ TEST_UTIL_H = \
test/util/logging.h \
test/util/mining.h \
test/util/net.h \
+ test/util/poolresourcetester.h \
test/util/random.h \
test/util/script.h \
test/util/setup_common.h \
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 8ecccd4d22..b679ad0602 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -8,6 +8,7 @@
#include <addrman.h>
#include <chainparams.h>
#include <clientversion.h>
+#include <common/args.h>
#include <cstdint>
#include <hash.h>
#include <logging.h>
@@ -21,7 +22,6 @@
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/settings.h>
-#include <util/system.h>
#include <util/translation.h>
namespace {
diff --git a/src/addrman.cpp b/src/addrman.cpp
index f5ca9a5c34..cdfd079fcd 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -58,9 +58,9 @@ int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGr
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
}
-int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) const
+int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int bucket) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << bucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
@@ -714,72 +714,98 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
}
}
-std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool newOnly) const
+std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
{
AssertLockHeld(cs);
if (vRandom.empty()) return {};
- if (newOnly && nNew == 0) return {};
-
- // Use a 50% chance for choosing between tried and new table entries.
- if (!newOnly &&
- (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
- // use a tried node
- double fChanceFactor = 1.0;
- while (1) {
- // Pick a tried bucket, and an initial position in that bucket.
- int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
- int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- // Iterate over the positions of that bucket, starting at the initial one,
- // and looping around.
- int i;
- for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- if (vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
- }
- // If the bucket is entirely empty, start over with a (likely) different one.
- if (i == ADDRMAN_BUCKET_SIZE) continue;
- // Find the entry to return.
- int nId = vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE];
- const auto it_found{mapInfo.find(nId)};
- assert(it_found != mapInfo.end());
- const AddrInfo& info{it_found->second};
- // With probability GetChance() * fChanceFactor, return the entry.
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
- LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToStringAddrPort());
- return {info, info.m_last_try};
- }
- // Otherwise start over with a (likely) different bucket, and increased chance factor.
- fChanceFactor *= 1.2;
- }
+ size_t new_count = nNew;
+ size_t tried_count = nTried;
+
+ if (network.has_value()) {
+ auto it = m_network_counts.find(*network);
+ if (it == m_network_counts.end()) return {};
+
+ auto counts = it->second;
+ new_count = counts.n_new;
+ tried_count = counts.n_tried;
+ }
+
+ if (new_only && new_count == 0) return {};
+ if (new_count + tried_count == 0) return {};
+
+ // Decide if we are going to search the new or tried table
+ // If either option is viable, use a 50% chance to choose
+ bool search_tried;
+ if (new_only || tried_count == 0) {
+ search_tried = false;
+ } else if (new_count == 0) {
+ search_tried = true;
} else {
- // use a new node
- double fChanceFactor = 1.0;
- while (1) {
- // Pick a new bucket, and an initial position in that bucket.
- int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
- int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- // Iterate over the positions of that bucket, starting at the initial one,
- // and looping around.
- int i;
- for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- if (vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
- }
- // If the bucket is entirely empty, start over with a (likely) different one.
- if (i == ADDRMAN_BUCKET_SIZE) continue;
- // Find the entry to return.
- int nId = vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE];
- const auto it_found{mapInfo.find(nId)};
- assert(it_found != mapInfo.end());
- const AddrInfo& info{it_found->second};
- // With probability GetChance() * fChanceFactor, return the entry.
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
- LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToStringAddrPort());
- return {info, info.m_last_try};
+ search_tried = insecure_rand.randbool();
+ }
+
+ const int bucket_count{search_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT};
+
+ // Loop through the addrman table until we find an appropriate entry
+ double chance_factor = 1.0;
+ while (1) {
+ // Pick a bucket, and an initial position in that bucket.
+ int bucket = insecure_rand.randrange(bucket_count);
+ int initial_position = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
+
+ // Iterate over the positions of that bucket, starting at the initial one,
+ // and looping around.
+ int i;
+ for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
+ int node_id = GetEntry(search_tried, bucket, position);
+ if (node_id != -1) {
+ if (network.has_value()) {
+ const auto it{mapInfo.find(node_id)};
+ assert(it != mapInfo.end());
+ const auto info{it->second};
+ if (info.GetNetwork() == *network) break;
+ } else {
+ break;
+ }
}
- // Otherwise start over with a (likely) different bucket, and increased chance factor.
- fChanceFactor *= 1.2;
}
+
+ // If the bucket is entirely empty, start over with a (likely) different one.
+ if (i == ADDRMAN_BUCKET_SIZE) continue;
+
+ // Find the entry to return.
+ int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
+ int nId = GetEntry(search_tried, bucket, position);
+ const auto it_found{mapInfo.find(nId)};
+ assert(it_found != mapInfo.end());
+ const AddrInfo& info{it_found->second};
+
+ // With probability GetChance() * chance_factor, return the entry.
+ if (insecure_rand.randbits(30) < chance_factor * info.GetChance() * (1 << 30)) {
+ LogPrint(BCLog::ADDRMAN, "Selected %s from %s\n", info.ToStringAddrPort(), search_tried ? "tried" : "new");
+ return {info, info.m_last_try};
+ }
+
+ // Otherwise start over with a (likely) different bucket, and increased chance factor.
+ chance_factor *= 1.2;
+ }
+}
+
+int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
+{
+ AssertLockHeld(cs);
+
+ assert(position < ADDRMAN_BUCKET_SIZE);
+
+ if (use_tried) {
+ assert(bucket < ADDRMAN_TRIED_BUCKET_COUNT);
+ return vvTried[bucket][position];
+ } else {
+ assert(bucket < ADDRMAN_NEW_BUCKET_COUNT);
+ return vvNew[bucket][position];
}
}
@@ -1164,11 +1190,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
return ret;
}
-std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool newOnly) const
+std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optional<Network> network) const
{
LOCK(cs);
Check();
- auto addrRet = Select_(newOnly);
+ auto addrRet = Select_(new_only, network);
Check();
return addrRet;
}
@@ -1262,9 +1288,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
return m_impl->SelectTriedCollision();
}
-std::pair<CAddress, NodeSeconds> AddrMan::Select(bool newOnly) const
+std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Network> network) const
{
- return m_impl->Select(newOnly);
+ return m_impl->Select(new_only, network);
}
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
diff --git a/src/addrman.h b/src/addrman.h
index 4985fc764c..6284b80a52 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -146,11 +146,14 @@ public:
/**
* Choose an address to connect to.
*
- * @param[in] newOnly Whether to only select addresses from the new table.
+ * @param[in] new_only Whether to only select addresses from the new table. Passing `true` returns
+ * an address from the new table or an empty pair. Passing `false` will return an
+ * address from either the new or tried table (it does not guarantee a tried entry).
+ * @param[in] network Select only addresses of this network (nullopt = all)
* @return CAddress The record for the selected peer.
* seconds The last time we attempted to connect to that peer.
*/
- std::pair<CAddress, NodeSeconds> Select(bool newOnly = false) const;
+ std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<Network> network = std::nullopt) const;
/**
* Return all or many randomly selected addresses, optionally by network.
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
index 94fe81aca9..7aead2812b 100644
--- a/src/addrman_impl.h
+++ b/src/addrman_impl.h
@@ -90,7 +90,7 @@ public:
}
//! Calculate in which position of a bucket to store this entry.
- int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
+ int GetBucketPosition(const uint256 &nKey, bool fNew, int bucket) const;
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
bool IsTerrible(NodeSeconds now = Now<NodeSeconds>()) const;
@@ -127,7 +127,7 @@ public:
std::pair<CAddress, NodeSeconds> SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
- std::pair<CAddress, NodeSeconds> Select(bool newOnly) const
+ std::pair<CAddress, NodeSeconds> Select(bool new_only, std::optional<Network> network) const
EXCLUSIVE_LOCKS_REQUIRED(!cs);
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
@@ -251,7 +251,13 @@ private:
void Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
- std::pair<CAddress, NodeSeconds> Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ std::pair<CAddress, NodeSeconds> Select_(bool new_only, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** Helper to generalize looking up an addrman entry from either table.
+ *
+ * @return int The nid of the entry or -1 if the addrman position is empty.
+ * */
+ int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/banman.cpp b/src/banman.cpp
index ece949d997..5b2049d654 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -5,6 +5,7 @@
#include <banman.h>
+#include <logging.h>
#include <netaddress.h>
#include <node/interface_ui.h>
#include <sync.h>
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index d6b52eb587..8a5cab443f 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -4,6 +4,7 @@
#include <addrman.h>
#include <bench/bench.h>
+#include <netbase.h>
#include <netgroup.h>
#include <random.h>
#include <util/check.h>
@@ -71,6 +72,20 @@ static void FillAddrMan(AddrMan& addrman)
AddAddressesToAddrMan(addrman);
}
+static CNetAddr ResolveIP(const std::string& ip)
+{
+ CNetAddr addr;
+ LookupHost(ip, addr, false);
+ return addr;
+}
+
+static CService ResolveService(const std::string& ip, uint16_t port = 0)
+{
+ CService serv;
+ Lookup(ip, serv, port, false);
+ return serv;
+}
+
/* Benchmarks */
static void AddrManAdd(benchmark::Bench& bench)
@@ -95,6 +110,41 @@ static void AddrManSelect(benchmark::Bench& bench)
});
}
+// The worst case performance of the Select() function is when there is only
+// one address on the table, because it linearly searches every position of
+// several buckets before identifying the correct bucket
+static void AddrManSelectFromAlmostEmpty(benchmark::Bench& bench)
+{
+ AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
+
+ // Add one address to the new table
+ CService addr = ResolveService("250.3.1.1", 8333);
+ addrman.Add({CAddress(addr, NODE_NONE)}, ResolveService("250.3.1.1", 8333));
+
+ bench.run([&] {
+ (void)addrman.Select();
+ });
+}
+
+static void AddrManSelectByNetwork(benchmark::Bench& bench)
+{
+ AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
+
+ // add single I2P address to new table
+ CService i2p_service;
+ i2p_service.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
+ CAddress i2p_address(i2p_service, NODE_NONE);
+ i2p_address.nTime = Now<NodeSeconds>();
+ CNetAddr source = ResolveIP("252.2.2.2");
+ addrman.Add({i2p_address}, source);
+
+ FillAddrMan(addrman);
+
+ bench.run([&] {
+ (void)addrman.Select(/*new_only=*/false, NET_I2P);
+ });
+}
+
static void AddrManGetAddr(benchmark::Bench& bench)
{
AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
@@ -135,5 +185,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
BENCHMARK(AddrManAdd, benchmark::PriorityLevel::HIGH);
BENCHMARK(AddrManSelect, benchmark::PriorityLevel::HIGH);
+BENCHMARK(AddrManSelectFromAlmostEmpty, benchmark::PriorityLevel::HIGH);
+BENCHMARK(AddrManSelectByNetwork, benchmark::PriorityLevel::HIGH);
BENCHMARK(AddrManGetAddr, benchmark::PriorityLevel::HIGH);
BENCHMARK(AddrManAddThenGood, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 7faaa1fb14..8c421c3fec 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -5,10 +5,10 @@
#include <bench/bench.h>
#include <clientversion.h>
+#include <common/args.h>
#include <crypto/sha256.h>
#include <util/fs.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <chrono>
#include <cstdint>
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index ee76f7b767..260c8991ce 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -6,9 +6,9 @@
#include <bench/data.h>
#include <chainparams.h>
+#include <common/args.h>
#include <consensus/validation.h>
#include <streams.h>
-#include <util/system.h>
#include <validation.h>
// These are the two major time-sinks which happen after we have fully received
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 265d4bf655..11ce14728d 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -5,6 +5,7 @@
#include <bench/bench.h>
#include <interfaces/chain.h>
#include <node/context.h>
+#include <policy/policy.h>
#include <wallet/coinselection.h>
#include <wallet/spend.h>
#include <wallet/wallet.h>
@@ -115,7 +116,7 @@ static void BnBExhaustion(benchmark::Bench& bench)
bench.run([&] {
// Benchmark
CAmount target = make_hard_case(17, utxo_pool);
- SelectCoinsBnB(utxo_pool, target, 0); // Should exhaust
+ SelectCoinsBnB(utxo_pool, target, 0, MAX_STANDARD_TX_WEIGHT); // Should exhaust
// Cleanup
utxo_pool.clear();
diff --git a/src/bench/pool.cpp b/src/bench/pool.cpp
new file mode 100644
index 0000000000..b3e54d85a2
--- /dev/null
+++ b/src/bench/pool.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2022 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 <bench/bench.h>
+#include <support/allocators/pool.h>
+
+#include <unordered_map>
+
+template <typename Map>
+void BenchFillClearMap(benchmark::Bench& bench, Map& map)
+{
+ size_t batch_size = 5000;
+
+ // make sure each iteration of the benchmark contains exactly 5000 inserts and one clear.
+ // do this at least 10 times so we get reasonable accurate results
+
+ bench.batch(batch_size).minEpochIterations(10).run([&] {
+ auto rng = ankerl::nanobench::Rng(1234);
+ for (size_t i = 0; i < batch_size; ++i) {
+ map[rng()];
+ }
+ map.clear();
+ });
+}
+
+static void PoolAllocator_StdUnorderedMap(benchmark::Bench& bench)
+{
+ auto map = std::unordered_map<uint64_t, uint64_t>();
+ BenchFillClearMap(bench, map);
+}
+
+static void PoolAllocator_StdUnorderedMapWithPoolResource(benchmark::Bench& bench)
+{
+ using Map = std::unordered_map<uint64_t,
+ uint64_t,
+ std::hash<uint64_t>,
+ std::equal_to<uint64_t>,
+ PoolAllocator<std::pair<const uint64_t, uint64_t>,
+ sizeof(std::pair<const uint64_t, uint64_t>) + 4 * sizeof(void*),
+ alignof(void*)>>;
+
+ // make sure the resource supports large enough pools to hold the node. We do this by adding the size of a few pointers to it.
+ auto pool_resource = Map::allocator_type::ResourceType();
+ auto map = Map{0, std::hash<uint64_t>{}, std::equal_to<uint64_t>{}, &pool_resource};
+ BenchFillClearMap(bench, map);
+}
+
+BENCHMARK(PoolAllocator_StdUnorderedMap, benchmark::PriorityLevel::HIGH);
+BENCHMARK(PoolAllocator_StdUnorderedMapWithPoolResource, benchmark::PriorityLevel::HIGH);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index abe3af70c3..04f6aae4f7 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -17,6 +17,7 @@
#include <kernel/validation_cache_sizes.h>
#include <chainparams.h>
+#include <common/args.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <node/blockstorage.h>
@@ -24,7 +25,6 @@
#include <node/chainstate.h>
#include <scheduler.h>
#include <script/sigcache.h>
-#include <util/system.h>
#include <util/thread.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 94ed765455..63ba6a935d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,6 +9,7 @@
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <common/args.h>
#include <common/url.h>
#include <compat/compat.h>
#include <compat/stdin.h>
@@ -22,6 +23,7 @@
#include <util/exception.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/time.h>
#include <util/translation.h>
#include <algorithm>
@@ -1228,7 +1230,7 @@ static int CommandLineRPC(int argc, char *argv[])
MAIN_FUNCTION
{
#ifdef WIN32
- util::WinCmdLineArgs winArgs;
+ common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index a05443a2e5..c35a111313 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -8,6 +8,7 @@
#include <clientversion.h>
#include <coins.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp
index c0ba4df2be..e3f70e1b76 100644
--- a/src/bitcoin-util.cpp
+++ b/src/bitcoin-util.cpp
@@ -11,6 +11,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <core_io.h>
#include <streams.h>
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 1ebe98b920..5dbf8a8616 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -9,6 +9,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <common/args.h>
#include <common/url.h>
#include <compat/compat.h>
#include <interfaces/init.h>
@@ -99,7 +100,7 @@ MAIN_FUNCTION
{
ArgsManager& args = gArgs;
#ifdef WIN32
- util::WinCmdLineArgs winArgs;
+ common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 2ff34125e1..e476b06017 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -9,6 +9,7 @@
#include <chainparams.h>
#include <clientversion.h>
+#include <common/args.h>
#include <common/init.h>
#include <common/url.h>
#include <compat/compat.h>
@@ -247,7 +248,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
MAIN_FUNCTION
{
#ifdef WIN32
- util::WinCmdLineArgs winArgs;
+ common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 6f48ee41b3..d3d358a3f0 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -6,12 +6,13 @@
#include <chainparams.h>
#include <chainparamsseeds.h>
+#include <common/args.h>
#include <consensus/merkle.h>
#include <deploymentinfo.h>
#include <hash.h> // for signet block challenge hash
+#include <logging.h>
#include <script/interpreter.h>
#include <util/string.h>
-#include <util/system.h>
#include <assert.h>
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 71978081ce..eb7b31923d 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -5,8 +5,8 @@
#include <chainparamsbase.h>
+#include <common/args.h>
#include <tinyformat.h>
-#include <util/system.h>
#include <assert.h>
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index f065647d49..dca6028c63 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -7,857 +7,895 @@
* Each line contains a BIP155 serialized (networkID, addr, port) tuple.
*/
static const uint8_t chainparams_seed_main[] = {
- 0x01,0x04,0x02,0x03,0x19,0xb5,0x20,0x8d,
- 0x01,0x04,0x02,0x98,0x4e,0x7c,0x20,0x8d,
- 0x01,0x04,0x05,0x27,0x4a,0xa6,0x20,0x8d,
- 0x01,0x04,0x05,0x2d,0x4f,0x51,0x47,0x9c,
- 0x01,0x04,0x05,0x35,0x10,0x80,0x20,0x8d,
- 0x01,0x04,0x05,0x5f,0xba,0x4e,0x20,0x8d,
+ 0x01,0x04,0x01,0x41,0xc3,0x62,0x20,0x8d,
+ 0x01,0x04,0x02,0x3b,0xec,0x38,0x20,0x8d,
+ 0x01,0x04,0x02,0x53,0x72,0x14,0x20,0x8d,
+ 0x01,0x04,0x02,0xf8,0xc2,0x10,0x20,0x8d,
+ 0x01,0x04,0x05,0x02,0x9a,0x06,0x20,0x8d,
+ 0x01,0x04,0x05,0x65,0x8c,0x1e,0x20,0x8d,
0x01,0x04,0x05,0x80,0x57,0x7e,0x20,0x8d,
- 0x01,0x04,0x05,0x85,0x41,0x52,0x20,0x8d,
- 0x01,0x04,0x05,0x92,0x14,0xe5,0x20,0x8d,
- 0x01,0x04,0x05,0xb4,0x29,0x77,0x20,0x8d,
+ 0x01,0x04,0x05,0x90,0x15,0x31,0x20,0x8d,
+ 0x01,0x04,0x05,0xac,0x84,0x68,0x20,0x8d,
0x01,0x04,0x05,0xbc,0x3e,0x12,0x20,0x8d,
- 0x01,0x04,0x05,0xc7,0xad,0x42,0x20,0x8d,
- 0x01,0x04,0x05,0xff,0x61,0x19,0x20,0x8d,
- 0x01,0x04,0x05,0xff,0x67,0xb4,0x20,0x8d,
- 0x01,0x04,0x08,0xd1,0x46,0x4d,0x20,0x8d,
+ 0x01,0x04,0x05,0xc8,0x02,0xb4,0x20,0x8d,
+ 0x01,0x04,0x08,0x81,0xb8,0xff,0x20,0x8d,
0x01,0x04,0x08,0xd1,0x69,0x8a,0x20,0x8d,
- 0x01,0x04,0x12,0xa2,0xd0,0x99,0xbc,0xcc,
- 0x01,0x04,0x17,0xaf,0x00,0xc8,0x20,0x8d,
+ 0x01,0x04,0x0c,0x22,0x62,0x94,0x20,0x8d,
+ 0x01,0x04,0x0e,0xc7,0x66,0x97,0x20,0x8d,
+ 0x01,0x04,0x12,0x1b,0x4f,0x11,0x20,0x8d,
+ 0x01,0x04,0x12,0x1b,0x7c,0xe7,0x20,0x8d,
+ 0x01,0x04,0x12,0xd8,0xf9,0x97,0x20,0x8d,
+ 0x01,0x04,0x17,0x58,0x9b,0x3a,0x20,0x8d,
+ 0x01,0x04,0x17,0x5d,0x65,0x9e,0x20,0x8d,
+ 0x01,0x04,0x17,0x6d,0x9c,0x4c,0x20,0x8d,
+ 0x01,0x04,0x17,0xaf,0x00,0xdc,0x20,0x8d,
0x01,0x04,0x17,0xaf,0x00,0xde,0x20,0x8d,
- 0x01,0x04,0x17,0xe9,0x6b,0x15,0x20,0x8d,
- 0x01,0x04,0x17,0xec,0x19,0xa9,0x20,0x8d,
- 0x01,0x04,0x18,0x23,0x44,0xe5,0x20,0x8d,
- 0x01,0x04,0x18,0x54,0xa4,0x32,0x20,0x8d,
- 0x01,0x04,0x18,0x74,0x99,0x73,0x20,0x8d,
- 0x01,0x04,0x18,0xb8,0x00,0x92,0x20,0x8d,
- 0x01,0x04,0x1b,0x21,0xa0,0xc4,0x20,0x8d,
+ 0x01,0x04,0x18,0xe8,0x24,0xe1,0x20,0x8d,
0x01,0x04,0x1b,0x7c,0x6c,0x13,0x20,0x8d,
0x01,0x04,0x1b,0x94,0xce,0x8c,0x20,0x8d,
- 0x01,0x04,0x1f,0x11,0x40,0xc0,0x20,0x8d,
- 0x01,0x04,0x1f,0x12,0x72,0x87,0x20,0x8d,
+ 0x01,0x04,0x1f,0x07,0x46,0xc3,0x20,0x8d,
+ 0x01,0x04,0x1f,0x19,0x62,0x10,0x20,0x8d,
0x01,0x04,0x1f,0x29,0x17,0xf9,0x20,0x8d,
- 0x01,0x04,0x1f,0x2a,0xb0,0x8a,0x20,0x8d,
+ 0x01,0x04,0x1f,0x2f,0x66,0x5c,0x20,0x8d,
0x01,0x04,0x1f,0x2f,0xca,0x70,0x20,0x8d,
- 0x01,0x04,0x22,0x41,0x2d,0x9d,0x20,0x8d,
- 0x01,0x04,0x22,0x50,0x86,0x44,0x20,0x8d,
+ 0x01,0x04,0x1f,0xa5,0x4e,0x92,0x20,0x8d,
+ 0x01,0x04,0x1f,0xa5,0xe4,0x8a,0x20,0x8d,
+ 0x01,0x04,0x22,0x40,0x65,0x04,0x20,0x8d,
+ 0x01,0x04,0x22,0x69,0x13,0x61,0x20,0x8d,
+ 0x01,0x04,0x22,0x7e,0x6b,0xb3,0x20,0x8d,
0x01,0x04,0x22,0x7e,0x73,0x23,0x20,0x8d,
- 0x01,0x04,0x25,0x01,0xcc,0xe7,0x20,0x8d,
+ 0x01,0x04,0x23,0xf5,0xba,0x75,0x20,0x8d,
+ 0x01,0x04,0x25,0x0f,0x3c,0x90,0x20,0x8d,
+ 0x01,0x04,0x25,0x10,0x69,0x3f,0x20,0x8d,
0x01,0x04,0x25,0x78,0x9b,0x22,0x20,0x8d,
- 0x01,0x04,0x25,0x8f,0x76,0xae,0x20,0x8d,
+ 0x01,0x04,0x25,0x78,0xb3,0x1d,0x20,0x8d,
+ 0x01,0x04,0x25,0x8b,0x66,0x49,0x20,0x8d,
0x01,0x04,0x25,0xc1,0xe3,0x10,0x20,0x8d,
0x01,0x04,0x25,0xdc,0x87,0x97,0x20,0x8d,
- 0x01,0x04,0x25,0xeb,0x92,0xec,0x20,0x8d,
- 0x01,0x04,0x26,0x7c,0x7e,0x2a,0x20,0x8d,
+ 0x01,0x04,0x26,0x35,0x81,0x43,0x20,0x8d,
+ 0x01,0x04,0x26,0x36,0x0e,0x59,0x20,0x8d,
0x01,0x04,0x26,0x8d,0x86,0x8c,0x20,0x8d,
0x01,0x04,0x26,0x91,0x97,0x96,0x20,0x8d,
- 0x01,0x04,0x28,0x73,0x89,0x1c,0x20,0x8d,
0x01,0x04,0x29,0x48,0x9a,0x42,0x20,0x8d,
- 0x01,0x04,0x29,0x4f,0x46,0x92,0x20,0x8d,
- 0x01,0x04,0x2a,0xc1,0x37,0x87,0x20,0x8d,
- 0x01,0x04,0x2b,0xe1,0x3e,0x6b,0x20,0x8d,
+ 0x01,0x04,0x2b,0x8f,0xcb,0xc6,0x20,0x8d,
+ 0x01,0x04,0x2d,0x0f,0x7c,0x75,0x20,0x8d,
0x01,0x04,0x2d,0x2b,0x61,0x67,0x20,0x8d,
- 0x01,0x04,0x2d,0x55,0x30,0x3a,0x20,0x8d,
- 0x01,0x04,0x2d,0x7e,0x1a,0xe5,0x20,0x8d,
+ 0x01,0x04,0x2d,0x2c,0xd5,0x74,0x20,0x8d,
+ 0x01,0x04,0x2d,0x3a,0xbb,0x65,0x20,0x8d,
+ 0x01,0x04,0x2d,0x4f,0xc0,0xec,0x20,0x8d,
+ 0x01,0x04,0x2d,0x51,0xf1,0x61,0x20,0x8d,
+ 0x01,0x04,0x2d,0x53,0xdc,0x66,0x20,0x8d,
+ 0x01,0x04,0x2d,0x53,0xf1,0x2e,0x20,0x8d,
+ 0x01,0x04,0x2d,0x57,0x6a,0x39,0x20,0x8d,
+ 0x01,0x04,0x2d,0x81,0x26,0x05,0x20,0x8d,
+ 0x01,0x04,0x2d,0x82,0x14,0xb1,0x20,0x8d,
0x01,0x04,0x2d,0x86,0x8e,0x28,0x20,0x8d,
- 0x01,0x04,0x2d,0x9a,0xfc,0xa2,0x20,0x8d,
- 0x01,0x04,0x2e,0x0d,0xd8,0xa9,0x20,0x8d,
+ 0x01,0x04,0x2d,0x87,0x04,0x8f,0x20,0x8d,
+ 0x01,0x04,0x2d,0x87,0x5c,0x7f,0x20,0x8d,
+ 0x01,0x04,0x2d,0x91,0xbc,0x70,0x20,0x8d,
0x01,0x04,0x2e,0x17,0x57,0xda,0x20,0x8d,
- 0x01,0x04,0x2e,0x28,0x7f,0xa4,0x20,0x8d,
- 0x01,0x04,0x2e,0x30,0x7e,0x3a,0x20,0x8d,
- 0x01,0x04,0x2e,0x3b,0x0d,0x23,0x20,0x8d,
- 0x01,0x04,0x2e,0x48,0xee,0x11,0x20,0x8d,
- 0x01,0x04,0x2e,0x80,0x8d,0xb8,0x20,0x8d,
- 0x01,0x04,0x2e,0x92,0xf8,0x59,0x20,0x8d,
- 0x01,0x04,0x2e,0xa5,0xdd,0xd1,0x24,0x75,
+ 0x01,0x04,0x2e,0x20,0x32,0x62,0x20,0x8d,
+ 0x01,0x04,0x2e,0x20,0x4e,0x11,0x20,0x8d,
+ 0x01,0x04,0x2e,0x3b,0x28,0x5b,0x20,0x8d,
+ 0x01,0x04,0x2e,0x8a,0xf6,0x4d,0x20,0x8d,
0x01,0x04,0x2e,0xa6,0x8e,0x02,0x20,0x8d,
+ 0x01,0x04,0x2e,0xa6,0xa2,0x3b,0x20,0x8d,
0x01,0x04,0x2e,0xaf,0xb2,0x03,0x20,0x8d,
- 0x01,0x04,0x2f,0x24,0x90,0x33,0x20,0x8d,
- 0x01,0x04,0x2f,0xb4,0x31,0x9e,0x20,0x8d,
- 0x01,0x04,0x31,0xe4,0x83,0x85,0x08,0xa2,
+ 0x01,0x04,0x2e,0xbc,0x0f,0x06,0x20,0x8d,
+ 0x01,0x04,0x2e,0xbc,0x1e,0x76,0x20,0x8d,
+ 0x01,0x04,0x2e,0xdf,0xdf,0xd8,0x20,0x8d,
+ 0x01,0x04,0x2e,0xe2,0x12,0x87,0x20,0x8d,
+ 0x01,0x04,0x2f,0x58,0x56,0x4f,0x20,0x8d,
+ 0x01,0x04,0x2f,0x94,0x07,0x45,0x20,0x8d,
+ 0x01,0x04,0x2f,0xc6,0xdf,0x3c,0x20,0x8d,
0x01,0x04,0x32,0x02,0x0d,0xa4,0x20,0x8d,
- 0x01,0x04,0x32,0x23,0x47,0x33,0x20,0x8d,
+ 0x01,0x04,0x32,0x04,0x87,0x54,0x20,0x8d,
+ 0x01,0x04,0x32,0x35,0x27,0xed,0x20,0x8d,
0x01,0x04,0x32,0x35,0xfa,0xa2,0x20,0x8d,
- 0x01,0x04,0x33,0x44,0x24,0x39,0x20,0x8d,
- 0x01,0x04,0x33,0x8a,0x04,0x87,0x75,0x31,
+ 0x01,0x04,0x32,0x44,0x79,0x2c,0x20,0x8d,
+ 0x01,0x04,0x32,0x75,0x84,0xb2,0x20,0x8d,
0x01,0x04,0x33,0x9a,0x3e,0x67,0x20,0x8d,
0x01,0x04,0x33,0x9e,0x96,0x9b,0x20,0x8d,
+ 0x01,0x04,0x33,0xfa,0x2e,0xd7,0x20,0x8d,
0x01,0x04,0x36,0xb0,0x3f,0x10,0x20,0x8d,
0x01,0x04,0x3a,0x9e,0x00,0x56,0x20,0x8d,
- 0x01,0x04,0x3b,0x8a,0x73,0x89,0x20,0x8d,
- 0x01,0x04,0x3b,0xa7,0xbf,0x3c,0x20,0x8d,
0x01,0x04,0x3c,0xcd,0xcd,0x77,0x20,0x8d,
- 0x01,0x04,0x3c,0xea,0x7a,0xf5,0x20,0x8d,
- 0x01,0x04,0x3c,0xf0,0xd2,0x9b,0x20,0x8d,
- 0x01,0x04,0x3d,0xef,0x5b,0xfa,0x20,0x8d,
- 0x01,0x04,0x3e,0x4a,0x8f,0x0b,0x20,0x8d,
- 0x01,0x04,0x3e,0x8a,0xa2,0x0c,0x20,0x8d,
- 0x01,0x04,0x3e,0xa9,0x4a,0xe9,0x20,0x8d,
+ 0x01,0x04,0x3d,0x4a,0x63,0xc1,0x20,0x8d,
+ 0x01,0x04,0x3d,0x5c,0x3b,0x68,0x20,0x8d,
+ 0x01,0x04,0x3e,0x7a,0xad,0xab,0x20,0x8d,
0x01,0x04,0x3e,0xab,0x81,0x20,0x20,0x8d,
- 0x01,0x04,0x3e,0xd1,0xc6,0x41,0x20,0x8d,
- 0x01,0x04,0x3f,0xf7,0x93,0xa6,0x20,0x8d,
- 0x01,0x04,0x40,0x62,0x4c,0x3e,0x20,0x8d,
+ 0x01,0x04,0x3e,0xb2,0x1b,0xef,0x20,0x8d,
+ 0x01,0x04,0x3e,0xd1,0xd2,0x03,0x20,0x8d,
+ 0x01,0x04,0x3e,0xd7,0x7f,0x49,0x20,0x8d,
+ 0x01,0x04,0x3e,0xee,0x94,0x68,0x20,0x8d,
+ 0x01,0x04,0x3e,0xf5,0x99,0x08,0x20,0x8d,
+ 0x01,0x04,0x40,0x92,0x88,0x2d,0x20,0x8d,
+ 0x01,0x04,0x41,0x15,0x86,0xb8,0x20,0x8d,
+ 0x01,0x04,0x42,0x12,0x0d,0x92,0x20,0x8d,
+ 0x01,0x04,0x42,0x17,0xe9,0x2b,0x20,0x8d,
+ 0x01,0x04,0x42,0x1b,0x62,0xd8,0x20,0x8d,
0x01,0x04,0x42,0x1d,0x81,0xda,0x20,0x8d,
- 0x01,0x04,0x42,0x60,0xeb,0x1c,0x20,0x8d,
- 0x01,0x04,0x42,0x82,0x78,0x34,0x20,0x8d,
- 0x01,0x04,0x42,0xc6,0xd1,0xf3,0x20,0x8d,
+ 0x01,0x04,0x42,0x26,0x5e,0x0d,0x20,0x8d,
+ 0x01,0x04,0x42,0x2d,0x8d,0x2e,0x20,0x8d,
+ 0x01,0x04,0x42,0x3a,0xf3,0xd7,0x20,0x8d,
+ 0x01,0x04,0x42,0x72,0x21,0x31,0x20,0x8d,
+ 0x01,0x04,0x42,0xc6,0xd3,0xa7,0x20,0x8d,
0x01,0x04,0x42,0xd0,0x40,0x80,0x20,0x8d,
- 0x01,0x04,0x42,0xe1,0xe7,0x94,0x20,0x8d,
- 0x01,0x04,0x43,0x37,0x03,0xc8,0x20,0x8d,
- 0x01,0x04,0x43,0x3a,0xe8,0x6b,0x20,0x8d,
- 0x01,0x04,0x43,0xd3,0x5c,0x02,0x20,0x8d,
- 0x01,0x04,0x43,0xdf,0x77,0x7a,0x20,0x8d,
- 0x01,0x04,0x44,0x30,0x83,0xfb,0x20,0x8d,
- 0x01,0x04,0x44,0xb5,0x04,0x0c,0x20,0x8d,
- 0x01,0x04,0x45,0x0e,0xb9,0x09,0x20,0x8d,
- 0x01,0x04,0x45,0x36,0x1d,0xc1,0x20,0x8d,
+ 0x01,0x04,0x42,0xdb,0xc4,0xaa,0x20,0x8d,
+ 0x01,0x04,0x43,0xd2,0xe4,0xcb,0x20,0x8d,
+ 0x01,0x04,0x44,0xb7,0x4b,0xfb,0x20,0x8d,
+ 0x01,0x04,0x44,0xc2,0x7d,0x8c,0x20,0x8d,
+ 0x01,0x04,0x44,0xc7,0x78,0x11,0x20,0x8d,
+ 0x01,0x04,0x45,0x04,0x5e,0xe2,0x20,0x8d,
+ 0x01,0x04,0x45,0x08,0xaf,0xc9,0x20,0x8d,
0x01,0x04,0x45,0x3b,0x12,0x16,0x20,0x8d,
- 0x01,0x04,0x45,0x83,0x65,0xb0,0x20,0x8d,
- 0x01,0x04,0x45,0xa5,0xcd,0x8e,0x22,0x81,
+ 0x01,0x04,0x45,0xc4,0x98,0x21,0x20,0x8d,
0x01,0x04,0x45,0xe4,0xdb,0x7c,0x20,0x8d,
- 0x01,0x04,0x46,0x3b,0x7b,0x19,0x20,0x8d,
- 0x01,0x04,0x46,0x3e,0x0d,0x96,0x20,0x8d,
- 0x01,0x04,0x46,0x42,0xf8,0xaa,0x20,0x8d,
- 0x01,0x04,0x46,0x70,0x99,0xe5,0x20,0x8d,
+ 0x01,0x04,0x46,0x40,0x1b,0x0c,0x20,0x8d,
0x01,0x04,0x46,0xa0,0xf0,0x84,0x20,0x8d,
- 0x01,0x04,0x46,0xbe,0xb1,0xcc,0x20,0x8d,
- 0x01,0x04,0x47,0x1c,0xbd,0xef,0x20,0x8d,
- 0x01,0x04,0x47,0xea,0x7d,0xc6,0x20,0x8d,
- 0x01,0x04,0x48,0x4a,0x7b,0xb3,0x20,0x8d,
- 0x01,0x04,0x48,0xfd,0xec,0xd9,0x20,0x8d,
- 0x01,0x04,0x49,0xdb,0xfe,0x78,0x20,0x8d,
+ 0x01,0x04,0x47,0x4f,0x6d,0x80,0x20,0x8d,
+ 0x01,0x04,0x47,0xb8,0xc1,0x4b,0x20,0x8d,
+ 0x01,0x04,0x48,0x0f,0x3b,0xad,0x20,0x8d,
+ 0x01,0x04,0x48,0x30,0xfd,0xa8,0x20,0x8d,
+ 0x01,0x04,0x48,0xcf,0xab,0xd2,0x20,0x8d,
+ 0x01,0x04,0x49,0x75,0x84,0x8a,0x20,0x8d,
+ 0x01,0x04,0x49,0xd4,0xe2,0x3b,0x20,0x8d,
+ 0x01,0x04,0x4a,0x4c,0x97,0x6e,0x20,0x8d,
0x01,0x04,0x4a,0x5b,0x73,0xe5,0x20,0x8d,
0x01,0x04,0x4a,0x76,0x89,0x77,0x20,0x8d,
- 0x01,0x04,0x4a,0xc3,0xa6,0x64,0x20,0x8d,
+ 0x01,0x04,0x4a,0xd5,0xaf,0x6c,0x20,0x8d,
+ 0x01,0x04,0x4a,0xd5,0xfb,0xef,0x20,0x8d,
0x01,0x04,0x4a,0xdc,0xff,0xbe,0x20,0x8d,
- 0x01,0x04,0x4c,0x43,0xd3,0x6e,0x20,0x8d,
- 0x01,0x04,0x4c,0xa9,0xa3,0x0e,0x20,0x8d,
- 0x01,0x04,0x4d,0x20,0x79,0xa2,0x20,0x8d,
- 0x01,0x04,0x4d,0x35,0x87,0x4a,0x20,0x8d,
+ 0x01,0x04,0x4a,0xdd,0xbd,0x6d,0x20,0x8d,
+ 0x01,0x04,0x4b,0x53,0xcb,0xe1,0x20,0x8d,
+ 0x01,0x04,0x4b,0xac,0x34,0xba,0x20,0x8d,
+ 0x01,0x04,0x4c,0x18,0x8f,0x16,0x20,0x8d,
+ 0x01,0x04,0x4c,0x45,0xca,0xf7,0x20,0x8d,
+ 0x01,0x04,0x4c,0x49,0xc6,0xf2,0x20,0x8d,
+ 0x01,0x04,0x4c,0x77,0xf8,0xf0,0x20,0x8d,
+ 0x01,0x04,0x4d,0x14,0x30,0x90,0x20,0x8d,
+ 0x01,0x04,0x4d,0x16,0x98,0xef,0x20,0x8d,
+ 0x01,0x04,0x4d,0x25,0xe0,0xde,0x20,0x8d,
+ 0x01,0x04,0x4d,0x30,0xc4,0xea,0x20,0x8d,
0x01,0x04,0x4d,0x46,0x10,0xf5,0x20,0x8d,
- 0x01,0x04,0x4d,0x55,0xcc,0x95,0x20,0x8d,
- 0x01,0x04,0x4d,0x6b,0x26,0xef,0x20,0x8d,
- 0x01,0x04,0x4d,0x78,0x1a,0x66,0x20,0x8d,
0x01,0x04,0x4d,0xa2,0xbe,0x5a,0x20,0x8d,
0x01,0x04,0x4e,0x14,0xe3,0xf9,0x20,0x8d,
0x01,0x04,0x4e,0x15,0xa7,0x08,0x20,0x8d,
- 0x01,0x04,0x4e,0x1b,0x8b,0x0d,0x20,0x8d,
- 0x01,0x04,0x4e,0x5a,0x5b,0xdc,0x20,0x8d,
+ 0x01,0x04,0x4e,0x23,0x93,0xcb,0x20,0x8d,
0x01,0x04,0x4e,0x6c,0x6c,0x19,0x20,0x8d,
- 0x01,0x04,0x4e,0x6c,0x6c,0x26,0x20,0x8d,
- 0x01,0x04,0x4f,0x4d,0xb6,0xb7,0x20,0x8d,
- 0x01,0x04,0x4f,0x62,0x9f,0x07,0x2c,0x45,
- 0x01,0x04,0x4f,0xbd,0xd3,0xc9,0x20,0x8d,
- 0x01,0x04,0x50,0x37,0xe1,0x9e,0x20,0x8d,
- 0x01,0x04,0x50,0x53,0xba,0x23,0x20,0x8d,
- 0x01,0x04,0x50,0x58,0xac,0xe3,0xfb,0x08,
- 0x01,0x04,0x50,0xd1,0x57,0x67,0x24,0x75,
+ 0x01,0x04,0x4e,0x9a,0xed,0x3c,0x20,0x8d,
+ 0x01,0x04,0x4f,0x0b,0x1f,0x4c,0x20,0x8d,
+ 0x01,0x04,0x4f,0x57,0x58,0xeb,0x20,0x8d,
+ 0x01,0x04,0x4f,0x65,0x01,0x19,0x20,0x8d,
+ 0x01,0x04,0x4f,0x7c,0x07,0xf1,0x20,0x8d,
+ 0x01,0x04,0x4f,0x7c,0x07,0xfd,0x20,0x8d,
+ 0x01,0x04,0x4f,0x96,0x44,0x2a,0x20,0x8d,
+ 0x01,0x04,0x4f,0xf9,0x0a,0x35,0x20,0x8d,
+ 0x01,0x04,0x50,0x52,0x15,0x4d,0x20,0x8d,
+ 0x01,0x04,0x50,0x52,0x4c,0x3b,0x20,0x8d,
+ 0x01,0x04,0x50,0x58,0xac,0xe3,0x20,0x8d,
+ 0x01,0x04,0x50,0x5d,0xd5,0xf6,0x20,0x8d,
+ 0x01,0x04,0x50,0x6f,0x8e,0xd5,0x20,0x8d,
+ 0x01,0x04,0x50,0xd0,0xe3,0x86,0x20,0x8d,
+ 0x01,0x04,0x50,0xd0,0xe4,0x09,0x20,0x8d,
+ 0x01,0x04,0x50,0xd1,0x40,0x56,0x20,0x8d,
0x01,0x04,0x50,0xe5,0x1c,0x3c,0x20,0x8d,
0x01,0x04,0x51,0x07,0x10,0xb6,0x20,0x8d,
- 0x01,0x04,0x51,0x07,0x11,0xca,0x20,0x8d,
0x01,0x04,0x51,0x13,0x0a,0x02,0x20,0x8d,
- 0x01,0x04,0x51,0x58,0xdd,0xbe,0x20,0x8d,
+ 0x01,0x04,0x51,0xa2,0xc4,0x2b,0x20,0x8d,
0x01,0x04,0x51,0xab,0x16,0x8f,0x20,0x8d,
+ 0x01,0x04,0x51,0xac,0xdd,0x04,0x20,0x8d,
0x01,0x04,0x51,0xe0,0x2c,0xa4,0x20,0x8d,
- 0x01,0x04,0x51,0xe0,0xa0,0x51,0x20,0x8d,
+ 0x01,0x04,0x51,0xf5,0x60,0x24,0x20,0x8d,
0x01,0x04,0x52,0x01,0x44,0x36,0x20,0x8d,
- 0x01,0x04,0x52,0x15,0xa4,0x2f,0x20,0x8d,
- 0x01,0x04,0x52,0x40,0x74,0x05,0x20,0x8d,
0x01,0x04,0x52,0x42,0x0a,0x0b,0x20,0x8d,
+ 0x01,0x04,0x52,0x42,0xd3,0x1f,0x20,0x8d,
+ 0x01,0x04,0x52,0x47,0x04,0x9a,0x20,0x8d,
0x01,0x04,0x52,0x60,0x60,0x28,0x20,0x8d,
0x01,0x04,0x52,0x74,0x32,0x65,0x20,0x8d,
- 0x01,0x04,0x52,0x81,0x44,0x3e,0x20,0x8d,
- 0x01,0x04,0x52,0x88,0x63,0x7a,0x20,0x8d,
- 0x01,0x04,0x52,0x9a,0x18,0xd1,0x20,0x8d,
- 0x01,0x04,0x52,0xc5,0xd7,0x7d,0x20,0x8d,
- 0x01,0x04,0x53,0x80,0x84,0x5b,0x20,0x8d,
+ 0x01,0x04,0x52,0x88,0x62,0xf9,0x20,0x8d,
+ 0x01,0x04,0x52,0xc3,0xed,0xfd,0x20,0x8d,
0x01,0x04,0x53,0x89,0x29,0x0a,0x20,0x8d,
- 0x01,0x04,0x53,0xd0,0x06,0xd3,0x20,0x8d,
+ 0x01,0x04,0x53,0xab,0xaf,0x05,0x20,0x8d,
0x01,0x04,0x53,0xd0,0xc1,0xf2,0x20,0x8d,
- 0x01,0x04,0x53,0xde,0x8a,0x55,0x20,0x8d,
- 0x01,0x04,0x53,0xf0,0x7c,0x44,0x20,0x8d,
- 0x01,0x04,0x53,0xf3,0xbf,0xc7,0x20,0x8d,
- 0x01,0x04,0x54,0x09,0x05,0xd3,0x20,0x8d,
- 0x01,0x04,0x54,0x1c,0x39,0x5a,0x20,0x8d,
+ 0x01,0x04,0x53,0xe9,0x4c,0xa5,0x20,0x8d,
+ 0x01,0x04,0x53,0xf0,0x59,0xc4,0x20,0x8d,
0x01,0x04,0x54,0x26,0x03,0xf9,0x20,0x8d,
- 0x01,0x04,0x54,0x70,0x3c,0x10,0x20,0x8d,
- 0x01,0x04,0x54,0xd7,0x38,0x77,0x20,0x8d,
- 0x01,0x04,0x54,0xe2,0xf3,0xaf,0x20,0x8d,
- 0x01,0x04,0x54,0xf5,0x0e,0x49,0x20,0x8d,
- 0x01,0x04,0x54,0xfc,0x9d,0x5a,0x47,0x9d,
+ 0x01,0x04,0x54,0x36,0x17,0x30,0x20,0x8d,
+ 0x01,0x04,0x54,0x7e,0xd8,0x4d,0x20,0x8d,
+ 0x01,0x04,0x54,0xd3,0xbb,0xd3,0x20,0x8d,
+ 0x01,0x04,0x54,0xf6,0xc8,0x7a,0x20,0x8d,
0x01,0x04,0x54,0xff,0xf4,0x3d,0x20,0x8d,
- 0x01,0x04,0x55,0x17,0x18,0x7b,0x20,0x8d,
- 0x01,0x04,0x55,0x34,0xb9,0x1d,0x21,0xda,
- 0x01,0x04,0x55,0x3a,0x78,0xc9,0x20,0x8d,
- 0x01,0x04,0x55,0x5d,0x60,0x12,0x20,0x8d,
- 0x01,0x04,0x55,0xa5,0x08,0xc5,0x20,0x8d,
- 0x01,0x04,0x55,0xad,0xa5,0x42,0x20,0x8d,
- 0x01,0x04,0x55,0xb8,0x8f,0x69,0x20,0x8d,
- 0x01,0x04,0x55,0xbf,0x4a,0x67,0x20,0x8d,
+ 0x01,0x04,0x55,0xa5,0x2a,0x73,0x20,0x8d,
0x01,0x04,0x55,0xc2,0xee,0x86,0x20,0x8d,
- 0x01,0x04,0x55,0xc3,0x36,0x6e,0x20,0x8d,
- 0x01,0x04,0x55,0xc3,0xc4,0x8e,0x20,0x8d,
- 0x01,0x04,0x55,0xd0,0x45,0x0b,0x20,0x8d,
0x01,0x04,0x55,0xd0,0x45,0x15,0x20,0x8d,
0x01,0x04,0x55,0xd0,0x47,0x24,0x20,0x8d,
- 0x01,0x04,0x55,0xd0,0x47,0x27,0x20,0x8d,
+ 0x01,0x04,0x55,0xd1,0xf0,0x5b,0x20,0x8d,
0x01,0x04,0x55,0xd6,0x76,0x47,0x20,0x8d,
0x01,0x04,0x55,0xd6,0xa1,0xfc,0x20,0x8d,
- 0x01,0x04,0x55,0xd8,0x20,0x49,0x20,0x8d,
- 0x01,0x04,0x55,0xfe,0x62,0xdd,0x20,0x8d,
- 0x01,0x04,0x56,0x3a,0x0b,0x98,0x20,0x8d,
+ 0x01,0x04,0x55,0xec,0xbe,0xfc,0x20,0x8d,
+ 0x01,0x04,0x55,0xf3,0x73,0x88,0x20,0x8d,
+ 0x01,0x04,0x56,0x16,0x14,0x0d,0x20,0x8d,
+ 0x01,0x04,0x56,0x31,0x22,0x5c,0x20,0x8d,
0x01,0x04,0x56,0x5f,0x08,0xf9,0x20,0x8d,
- 0x01,0x04,0x56,0x64,0x1a,0xbc,0x20,0x8d,
- 0x01,0x04,0x56,0x6a,0x8f,0x8f,0xd8,0x4d,
- 0x01,0x04,0x56,0x7c,0x91,0xb8,0x20,0x8d,
- 0x01,0x04,0x56,0x85,0xfb,0xef,0x22,0xc5,
+ 0x01,0x04,0x56,0x68,0xe4,0x0a,0x20,0x8d,
+ 0x01,0x04,0x56,0x68,0xe4,0x17,0x20,0x8d,
0x01,0x04,0x57,0x4f,0x5e,0xdd,0x20,0x8d,
- 0x01,0x04,0x57,0x78,0x08,0x05,0x4e,0x28,
- 0x01,0x04,0x57,0x7d,0x9d,0xdc,0x20,0x8d,
- 0x01,0x04,0x58,0x09,0x4c,0x85,0x20,0x8d,
- 0x01,0x04,0x58,0x5a,0xb8,0x44,0x20,0x8d,
- 0x01,0x04,0x58,0x97,0x65,0x0e,0x13,0x88,
- 0x01,0x04,0x58,0x97,0x65,0xfd,0x13,0x88,
- 0x01,0x04,0x58,0xc6,0x5c,0x2f,0x20,0x8d,
+ 0x01,0x04,0x58,0x0a,0x59,0x17,0x20,0x8d,
+ 0x01,0x04,0x58,0x54,0xdf,0x1e,0x20,0x8d,
+ 0x01,0x04,0x58,0x56,0x7d,0x32,0x20,0x8d,
+ 0x01,0x04,0x58,0x5a,0x4d,0x64,0x20,0x8d,
+ 0x01,0x04,0x58,0x61,0x28,0x32,0x20,0x8d,
+ 0x01,0x04,0x58,0x89,0x6d,0x3e,0x20,0x8d,
+ 0x01,0x04,0x58,0x93,0xf4,0xfa,0x20,0x8d,
0x01,0x04,0x58,0xd0,0x73,0x46,0x20,0x8d,
- 0x01,0x04,0x58,0xd2,0x0f,0x18,0x20,0x8d,
- 0x01,0x04,0x58,0xd4,0x2d,0xa6,0x20,0x8d,
- 0x01,0x04,0x59,0x66,0xce,0xee,0x20,0x8d,
- 0x01,0x04,0x59,0x67,0x6f,0x22,0x20,0x8d,
- 0x01,0x04,0x59,0x72,0x8f,0x71,0x20,0x8d,
- 0x01,0x04,0x59,0x86,0x3e,0x4a,0x20,0x8d,
- 0x01,0x04,0x59,0x98,0x08,0xe7,0x20,0x8d,
- 0x01,0x04,0x59,0xa1,0x1a,0x4e,0x20,0x8d,
- 0x01,0x04,0x59,0xcf,0x83,0x13,0x20,0x8d,
- 0x01,0x04,0x59,0xf8,0xc1,0xe5,0x20,0x8d,
- 0x01,0x04,0x5a,0x03,0x30,0x3e,0x20,0x8d,
- 0x01,0x04,0x5a,0x92,0x79,0x61,0x20,0x8d,
+ 0x01,0x04,0x58,0xd4,0x35,0xf6,0x20,0x8d,
+ 0x01,0x04,0x59,0x23,0x8e,0xa8,0x20,0x8d,
+ 0x01,0x04,0x59,0x4e,0x6f,0xc5,0x20,0x8d,
+ 0x01,0x04,0x59,0x75,0x3b,0x81,0x20,0x8d,
+ 0x01,0x04,0x59,0x93,0x6c,0xc8,0x20,0x8d,
+ 0x01,0x04,0x59,0xa3,0x84,0xb4,0x20,0x8d,
+ 0x01,0x04,0x59,0xa5,0xe8,0xf2,0x20,0x8d,
+ 0x01,0x04,0x59,0xd8,0x15,0x60,0x20,0x8d,
+ 0x01,0x04,0x5a,0x32,0xac,0xb6,0x20,0x8d,
0x01,0x04,0x5a,0x92,0x82,0xd6,0x20,0x8d,
- 0x01,0x04,0x5a,0xc4,0xa9,0x3a,0x20,0x8d,
- 0x01,0x04,0x5a,0xfa,0x09,0x01,0x20,0x8d,
+ 0x01,0x04,0x5a,0x92,0xd0,0xa2,0x20,0x8d,
+ 0x01,0x04,0x5a,0x9c,0x1a,0x94,0x20,0x8d,
+ 0x01,0x04,0x5a,0xa3,0xac,0x8b,0x20,0x8d,
+ 0x01,0x04,0x5a,0xb1,0xa3,0x4d,0x20,0x8d,
+ 0x01,0x04,0x5b,0x43,0x91,0x6e,0x20,0x8d,
0x01,0x04,0x5b,0x5d,0xc2,0x9a,0x20,0x8d,
- 0x01,0x04,0x5b,0x7e,0x28,0x6d,0x20,0x8d,
- 0x01,0x04,0x5b,0xcc,0x63,0xb2,0x20,0x8d,
+ 0x01,0x04,0x5b,0x7b,0xb6,0xa4,0x20,0x8d,
+ 0x01,0x04,0x5b,0x7b,0xb7,0xdb,0x20,0x8d,
+ 0x01,0x04,0x5b,0x87,0x00,0xbb,0x20,0x8d,
+ 0x01,0x04,0x5b,0x93,0xe8,0x62,0x20,0x8d,
+ 0x01,0x04,0x5b,0xb8,0xa8,0xf9,0x20,0x8d,
+ 0x01,0x04,0x5b,0xc1,0xed,0x74,0x20,0x8d,
+ 0x01,0x04,0x5b,0xc7,0x29,0x2d,0x20,0x8d,
0x01,0x04,0x5b,0xcc,0x95,0x05,0x20,0x8d,
- 0x01,0x04,0x5b,0xce,0x11,0xc3,0x20,0x8d,
- 0x01,0x04,0x5b,0xd1,0x33,0x83,0x20,0x8d,
0x01,0x04,0x5b,0xd7,0x5b,0xfe,0x20,0x8d,
- 0x01,0x04,0x5c,0x5b,0x1b,0x3c,0x20,0x8d,
+ 0x01,0x04,0x5b,0xdb,0x19,0xe8,0x20,0x8d,
+ 0x01,0x04,0x5b,0xed,0x58,0xda,0x20,0x8d,
+ 0x01,0x04,0x5c,0x1b,0x96,0x2e,0x20,0x8d,
+ 0x01,0x04,0x5c,0x1b,0x96,0x2f,0x20,0x8d,
0x01,0x04,0x5c,0xdd,0x14,0xe8,0x20,0x8d,
- 0x01,0x04,0x5c,0xff,0x55,0x1f,0x20,0x8d,
- 0x01,0x04,0x5d,0x04,0x65,0x25,0x20,0x8d,
- 0x01,0x04,0x5d,0x2e,0x51,0x05,0x20,0x8d,
- 0x01,0x04,0x5d,0x39,0x51,0xa2,0x20,0x8d,
- 0x01,0x04,0x5d,0x49,0x27,0xc4,0x20,0x8d,
- 0x01,0x04,0x5d,0x5a,0x52,0xe2,0x20,0x8d,
+ 0x01,0x04,0x5c,0xdd,0x7e,0x41,0x20,0x8d,
+ 0x01,0x04,0x5d,0x21,0xc0,0xcc,0x20,0x8d,
+ 0x01,0x04,0x5d,0x29,0xed,0x4e,0x20,0x8d,
0x01,0x04,0x5d,0x5f,0x58,0x0d,0x20,0x8d,
+ 0x01,0x04,0x5d,0x5f,0xe3,0x7d,0x20,0x8d,
+ 0x01,0x04,0x5d,0x67,0x0d,0x01,0x20,0x8d,
+ 0x01,0x04,0x5d,0x73,0x56,0xef,0x20,0x8d,
0x01,0x04,0x5d,0x7b,0xb4,0xa4,0x20,0x8d,
- 0x01,0x04,0x5d,0xbd,0x91,0xa9,0x20,0x8d,
- 0x01,0x04,0x5e,0x11,0xb9,0x6b,0x20,0x8d,
- 0x01,0x04,0x5e,0x4b,0xc6,0x78,0x20,0x8d,
- 0x01,0x04,0x5e,0x72,0xc4,0xa9,0x20,0x8d,
- 0x01,0x04,0x5e,0x8e,0xd5,0xfa,0xd8,0xf8,
+ 0x01,0x04,0x5d,0xba,0xc9,0xad,0x20,0x8d,
+ 0x01,0x04,0x5d,0xbe,0x75,0x1a,0x20,0x8d,
+ 0x01,0x04,0x5e,0x13,0x07,0x37,0x20,0x8d,
+ 0x01,0x04,0x5e,0x17,0x15,0x50,0x20,0x8d,
+ 0x01,0x04,0x5e,0x17,0xcd,0x6e,0x20,0x8d,
+ 0x01,0x04,0x5e,0x83,0x00,0x49,0x20,0x8d,
+ 0x01,0x04,0x5e,0x8e,0xed,0x04,0x20,0x8d,
0x01,0x04,0x5e,0x9a,0x9f,0x63,0x20,0x8d,
- 0x01,0x04,0x5e,0x9e,0xf6,0xb7,0x20,0x8d,
- 0x01,0x04,0x5e,0xef,0x91,0x20,0x20,0x8d,
- 0x01,0x04,0x5f,0x1f,0x0c,0x16,0x20,0x8d,
- 0x01,0x04,0x5f,0x1f,0xc4,0x0f,0x20,0x8d,
- 0x01,0x04,0x5f,0x6e,0x85,0xdf,0x20,0x8d,
+ 0x01,0x04,0x5e,0xca,0x32,0xc8,0x20,0x8d,
+ 0x01,0x04,0x5e,0xe7,0xfd,0x12,0x20,0x8d,
+ 0x01,0x04,0x5f,0x2a,0x8c,0x8e,0x20,0x8d,
+ 0x01,0x04,0x5f,0x43,0x12,0x64,0x20,0x8d,
+ 0x01,0x04,0x5f,0x46,0xee,0xb0,0x20,0x8d,
+ 0x01,0x04,0x5f,0x53,0x49,0x1f,0x20,0x8d,
+ 0x01,0x04,0x5f,0x5a,0x80,0x03,0x20,0x8d,
0x01,0x04,0x5f,0x6e,0xea,0x5d,0x20,0x8d,
0x01,0x04,0x5f,0xa1,0x0c,0x2d,0x20,0x8d,
+ 0x01,0x04,0x5f,0xac,0x3e,0xa7,0x20,0x8d,
+ 0x01,0x04,0x5f,0xb3,0x80,0x57,0x20,0x8d,
0x01,0x04,0x5f,0xbf,0x82,0x64,0x20,0x8d,
- 0x01,0x04,0x5f,0xd0,0x9e,0xa1,0x20,0x8d,
- 0x01,0x04,0x5f,0xd5,0x91,0xda,0x20,0x8d,
0x01,0x04,0x5f,0xd6,0x35,0x9a,0x20,0x8d,
- 0x01,0x04,0x5f,0xd6,0x35,0xa0,0x20,0x8d,
- 0x01,0x04,0x60,0x2c,0x9c,0xc7,0x20,0x8d,
+ 0x01,0x04,0x60,0x03,0x35,0xfe,0x20,0x8d,
0x01,0x04,0x61,0x4b,0x91,0x0c,0x20,0x8d,
+ 0x01,0x04,0x61,0x51,0xc6,0xb4,0x20,0x8d,
+ 0x01,0x04,0x61,0x57,0xd8,0x6e,0x20,0x8d,
+ 0x01,0x04,0x63,0xe5,0xd2,0x6f,0x20,0x8d,
+ 0x01,0x04,0x63,0xf6,0x57,0x02,0x20,0x8d,
+ 0x01,0x04,0x65,0x2b,0x7c,0xc3,0x20,0x8d,
0x01,0x04,0x66,0x84,0xc0,0x8d,0x20,0x8d,
- 0x01,0x04,0x67,0x0e,0xf5,0xfa,0x20,0x8d,
- 0x01,0x04,0x67,0x55,0x26,0xcd,0x20,0x8d,
- 0x01,0x04,0x67,0x58,0x5c,0x4e,0x20,0x8c,
+ 0x01,0x04,0x67,0x15,0x03,0x59,0x20,0x8d,
+ 0x01,0x04,0x67,0x23,0x79,0x48,0x20,0x8d,
0x01,0x04,0x67,0x63,0xa8,0x64,0x20,0x8d,
0x01,0x04,0x67,0x63,0xa8,0x8c,0x20,0x8d,
0x01,0x04,0x67,0x63,0xaa,0xd2,0x20,0x8d,
0x01,0x04,0x67,0x63,0xaa,0xdc,0x20,0x8d,
- 0x01,0x04,0x67,0x64,0x2c,0x46,0x20,0x8d,
- 0x01,0x04,0x67,0xb2,0xec,0x1b,0x20,0x8d,
- 0x01,0x04,0x67,0xd1,0x0c,0x90,0x20,0x8d,
- 0x01,0x04,0x68,0x3b,0x93,0x0f,0x20,0x8d,
- 0x01,0x04,0x68,0x81,0xab,0x79,0x20,0x8d,
- 0x01,0x04,0x68,0xc8,0x41,0xea,0x20,0x8d,
+ 0x01,0x04,0x67,0x69,0xca,0x32,0x20,0x8d,
0x01,0x04,0x68,0xee,0xdc,0xc7,0x20,0x8d,
+ 0x01,0x04,0x68,0xf3,0x21,0xa5,0x20,0x8d,
0x01,0x04,0x68,0xf4,0x49,0x06,0x20,0x8d,
- 0x01,0x04,0x6a,0x47,0x77,0xe6,0x20,0x8d,
- 0x01,0x04,0x6b,0xad,0xa6,0x2b,0x20,0x8d,
- 0x01,0x04,0x6c,0xa1,0x16,0x4e,0x20,0x8d,
- 0x01,0x04,0x6c,0xae,0x3f,0xea,0x20,0x8d,
+ 0x01,0x04,0x6c,0x1a,0x7d,0xd6,0x20,0x8d,
+ 0x01,0x04,0x6d,0x56,0x3c,0x21,0x20,0x8d,
0x01,0x04,0x6d,0x63,0x3f,0x9f,0x20,0x8d,
- 0x01,0x04,0x6d,0x69,0x28,0xf7,0x20,0x8d,
- 0x01,0x04,0x6d,0x6b,0xb9,0x82,0x20,0x8d,
- 0x01,0x04,0x6d,0x6e,0xef,0x04,0x20,0x8d,
- 0x01,0x04,0x6d,0xad,0x29,0x2b,0x20,0x8d,
+ 0x01,0x04,0x6d,0x78,0xc2,0x88,0x20,0x8d,
+ 0x01,0x04,0x6d,0x7b,0xe9,0x8a,0x20,0x8d,
+ 0x01,0x04,0x6d,0x7b,0xf0,0x35,0x20,0x8d,
+ 0x01,0x04,0x6d,0x99,0x5e,0x23,0x20,0x8d,
+ 0x01,0x04,0x6d,0xad,0x7e,0x9d,0x20,0x8d,
+ 0x01,0x04,0x6d,0xc1,0x4c,0xc8,0x20,0x8d,
+ 0x01,0x04,0x6d,0xdd,0xe5,0xc5,0x20,0x8d,
0x01,0x04,0x6d,0xec,0x5a,0x75,0x20,0x8d,
0x01,0x04,0x6d,0xf8,0xce,0x0d,0x20,0x8d,
- 0x01,0x04,0x6d,0xff,0x6a,0xce,0x20,0x8d,
0x01,0x04,0x6f,0x5a,0x8c,0x17,0x20,0x8d,
0x01,0x04,0x6f,0x5a,0x8c,0x2e,0x20,0x8d,
- 0x01,0x04,0x6f,0x5a,0x9f,0xf6,0x20,0x8d,
- 0x01,0x04,0x70,0x76,0xbc,0x32,0x20,0x8d,
- 0x01,0x04,0x73,0x2f,0x8d,0xfa,0x22,0xb5,
+ 0x01,0x04,0x6f,0x5a,0x91,0x25,0x20,0x8d,
+ 0x01,0x04,0x72,0xad,0x9f,0xd1,0x20,0x8d,
0x01,0x04,0x74,0x3a,0xab,0x43,0x20,0x8d,
- 0x01,0x04,0x76,0x5c,0x6b,0x6c,0x20,0x8d,
+ 0x01,0x04,0x77,0x1f,0xb3,0xca,0x20,0x8d,
0x01,0x04,0x77,0x2a,0x37,0xcb,0x20,0x8d,
- 0x01,0x04,0x78,0x4f,0x47,0x48,0x20,0x8d,
- 0x01,0x04,0x79,0x63,0xf0,0x57,0x20,0x8d,
+ 0x01,0x04,0x7a,0xde,0xa0,0xbe,0x20,0x8d,
0x01,0x04,0x7b,0x3c,0xd5,0xc0,0x20,0x8d,
- 0x01,0x04,0x7c,0x9c,0x9e,0x64,0x20,0x8d,
- 0x01,0x04,0x7c,0xde,0x7b,0xee,0x20,0x8d,
- 0x01,0x04,0x7d,0xb2,0x06,0x74,0x20,0x8d,
+ 0x01,0x04,0x7c,0xc5,0x36,0x71,0x20,0x8d,
+ 0x01,0x04,0x7d,0xa8,0x8c,0x6c,0x20,0x8d,
0x01,0x04,0x80,0x00,0xbe,0x1a,0x20,0x8d,
0x01,0x04,0x80,0x41,0xc2,0x88,0x20,0x8d,
0x01,0x04,0x81,0x0d,0xbd,0xd4,0x20,0x8d,
- 0x01,0x04,0x81,0x7e,0xac,0x73,0x20,0x8d,
- 0x01,0x04,0x81,0x92,0x34,0xae,0x20,0x8d,
- 0x01,0x04,0x82,0x2c,0xa8,0xca,0x20,0x8d,
- 0x01,0x04,0x83,0xa1,0x50,0xa6,0x20,0x8d,
+ 0x01,0x04,0x81,0x0d,0xbd,0xd7,0x20,0x8d,
+ 0x01,0x04,0x81,0xe2,0xd8,0x94,0x20,0x8d,
0x01,0x04,0x83,0xbc,0x28,0xbf,0x20,0x8d,
+ 0x01,0x04,0x86,0x41,0x09,0x3f,0x20,0x8d,
+ 0x01,0x04,0x86,0x7a,0xc8,0xa0,0x20,0x8d,
0x01,0x04,0x86,0xc3,0xb9,0x34,0x20,0x8d,
- 0x01,0x04,0x87,0x86,0xee,0x2f,0x20,0x8d,
- 0x01,0x04,0x87,0xb4,0xda,0x3a,0x20,0x8d,
- 0x01,0x04,0x87,0xb5,0xd7,0xed,0x20,0x8d,
- 0x01,0x04,0x88,0x1d,0x6d,0xb4,0x20,0x8d,
+ 0x01,0x04,0x87,0x13,0xfd,0x65,0x20,0x8d,
+ 0x01,0x04,0x88,0x1d,0x6d,0x3a,0x20,0x8d,
0x01,0x04,0x88,0x20,0xee,0x06,0x20,0x8d,
- 0x01,0x04,0x88,0x38,0xaa,0x60,0x20,0x8d,
- 0x01,0x04,0x89,0x19,0x26,0x6c,0x20,0x8d,
+ 0x01,0x04,0x88,0x31,0xc9,0x18,0x20,0x8d,
0x01,0x04,0x89,0xe2,0x22,0x2e,0x20,0x8d,
- 0x01,0x04,0x8a,0xcf,0xd3,0x6a,0x20,0x8d,
+ 0x01,0x04,0x8a,0xcf,0xd3,0xbd,0x20,0x8d,
0x01,0x04,0x8b,0x82,0x29,0x52,0x20,0x8d,
- 0x01,0x04,0x8b,0x99,0xff,0x6b,0x20,0x8d,
- 0x01,0x04,0x8c,0xbe,0x0c,0x81,0x20,0x8d,
+ 0x01,0x04,0x8c,0xee,0xdc,0x63,0x20,0x8d,
0x01,0x04,0x8e,0x36,0xb5,0xda,0x20,0x8d,
+ 0x01,0x04,0x8e,0xa6,0x13,0x17,0x20,0x8d,
+ 0x01,0x04,0x8e,0xfe,0x57,0x73,0x20,0x8d,
0x01,0x04,0x8f,0xb1,0xe5,0x95,0x20,0x8d,
- 0x01,0x04,0x8f,0xb2,0x40,0x0a,0x20,0x8d,
- 0x01,0x04,0x90,0x18,0xf5,0xb7,0x20,0x8d,
- 0x01,0x04,0x90,0x7e,0x82,0xb2,0x20,0x8d,
- 0x01,0x04,0x92,0x04,0x7c,0x81,0x20,0x8d,
+ 0x01,0x04,0x90,0x02,0x65,0x15,0x20,0x8d,
+ 0x01,0x04,0x90,0x18,0xec,0x40,0x20,0x8d,
+ 0x01,0x04,0x91,0x28,0x33,0x34,0x20,0x8d,
0x01,0x04,0x92,0x47,0x45,0x67,0x20,0x8d,
- 0x01,0x04,0x92,0x53,0x38,0x45,0x20,0x8d,
- 0x01,0x04,0x93,0xc2,0xb1,0xa5,0x20,0x8d,
- 0x01,0x04,0x95,0x5a,0xd6,0x4e,0x20,0x8d,
- 0x01,0x04,0x95,0x66,0x9d,0x9c,0x20,0x8d,
- 0x01,0x04,0x97,0xf8,0x9c,0x37,0x20,0x8d,
- 0x01,0x04,0x97,0xfc,0xc1,0xf5,0x20,0x8d,
- 0x01,0x04,0x99,0x5c,0x5d,0x72,0x20,0x8d,
- 0x01,0x04,0x9a,0xd3,0x06,0x02,0x20,0x8d,
- 0x01,0x04,0x9c,0x11,0x67,0x02,0x1f,0x98,
+ 0x01,0x04,0x92,0x78,0xf1,0xad,0x20,0x8d,
+ 0x01,0x04,0x93,0x32,0xee,0x35,0x20,0x8d,
+ 0x01,0x04,0x94,0x67,0x65,0x84,0x20,0x8d,
+ 0x01,0x04,0x95,0x4b,0x30,0x5c,0x20,0x8d,
+ 0x01,0x04,0x98,0x2c,0x89,0x53,0x20,0x8d,
+ 0x01,0x04,0x9a,0x00,0x03,0xc2,0x20,0x8d,
+ 0x01,0x04,0x9a,0x1a,0x89,0x69,0x20,0x8d,
+ 0x01,0x04,0x9a,0x1a,0x9a,0x49,0x20,0x8d,
+ 0x01,0x04,0x9a,0x39,0x05,0x0b,0x20,0x8d,
+ 0x01,0x04,0x9b,0x04,0x37,0x15,0x20,0x8d,
+ 0x01,0x04,0x9c,0x92,0x89,0x8e,0x20,0x8d,
0x01,0x04,0x9c,0x92,0xb1,0xdd,0x20,0x8d,
- 0x01,0x04,0x9d,0x83,0x8f,0xad,0x20,0x8d,
- 0x01,0x04,0x9e,0x3a,0xbc,0x25,0x20,0x8d,
- 0x01,0x04,0x9e,0xf8,0x27,0xef,0x20,0x8d,
- 0x01,0x04,0x9f,0x59,0xe6,0x80,0x20,0x8d,
+ 0x01,0x04,0x9d,0x16,0x48,0xaf,0x20,0x8d,
+ 0x01,0x04,0x9d,0x61,0x00,0x76,0x20,0x8d,
+ 0x01,0x04,0x9e,0x8c,0x8d,0x45,0x20,0x8d,
+ 0x01,0x04,0x9e,0xb5,0x84,0x54,0x20,0x8d,
+ 0x01,0x04,0x9f,0x02,0xd7,0x62,0x20,0x8d,
0x01,0x04,0x9f,0xc4,0x03,0xef,0x20,0x8d,
0x01,0x04,0x9f,0xe0,0xbd,0xfa,0x20,0x8d,
- 0x01,0x04,0xa0,0x48,0x33,0x9a,0x20,0x8d,
- 0x01,0x04,0xa1,0x1d,0xec,0x37,0x20,0x8d,
- 0x01,0x04,0xa1,0x61,0x77,0xa6,0x20,0x8d,
+ 0x01,0x04,0xa0,0x50,0x0c,0x10,0x20,0x8d,
+ 0x01,0x04,0xa1,0xe6,0x26,0xa0,0x20,0x8d,
0x01,0x04,0xa1,0xf6,0x0b,0xe6,0x20,0x8d,
+ 0x01,0x04,0xa2,0x00,0xd2,0x98,0x20,0x8d,
0x01,0x04,0xa2,0x3e,0x12,0xe2,0x20,0x8d,
- 0x01,0x04,0xa2,0xfa,0x7b,0xb3,0x20,0x8d,
- 0x01,0x04,0xa2,0xfa,0xbf,0xde,0x20,0x8d,
0x01,0x04,0xa2,0xfe,0x76,0x14,0x20,0x8d,
- 0x01,0x04,0xa3,0xac,0x51,0x46,0x20,0x8d,
- 0x01,0x04,0xa4,0x5a,0x2f,0x08,0x20,0x8d,
+ 0x01,0x04,0xa3,0x9e,0xa8,0xb5,0x20,0x8d,
+ 0x01,0x04,0xa5,0xad,0x13,0x21,0x20,0x8d,
0x01,0x04,0xa5,0xe4,0xae,0x75,0x20,0x8d,
- 0x01,0x04,0xa6,0x46,0x91,0x97,0x20,0x8d,
- 0x01,0x04,0xa8,0x5b,0xee,0x08,0x20,0x8d,
- 0x01,0x04,0xaa,0xfd,0x0b,0x19,0x20,0x8d,
- 0x01,0x04,0xab,0x67,0xaa,0x73,0x20,0x8d,
- 0x01,0x04,0xac,0x5d,0xa6,0x87,0x20,0x8d,
- 0x01,0x04,0xac,0x67,0xd9,0xec,0x20,0x8d,
+ 0x01,0x04,0xa5,0xff,0xf1,0xb8,0x20,0x8d,
+ 0x01,0x04,0xa7,0x58,0x0b,0xcb,0x20,0x8d,
+ 0x01,0x04,0xa7,0xb3,0x93,0x9b,0x20,0x8d,
+ 0x01,0x04,0xaa,0x11,0x97,0xeb,0x20,0x8d,
+ 0x01,0x04,0xaa,0x40,0xae,0xe6,0x20,0x8d,
+ 0x01,0x04,0xac,0x5c,0x66,0x73,0x20,0x8d,
0x01,0x04,0xac,0x69,0x15,0xd8,0x20,0x8d,
- 0x01,0x04,0xac,0x70,0x99,0x5f,0x20,0x8d,
- 0x01,0x04,0xad,0x03,0xda,0x5b,0x20,0x8d,
- 0x01,0x04,0xad,0x0c,0x77,0x85,0x20,0x8d,
- 0x01,0x04,0xad,0x22,0x7f,0xb5,0x20,0x8d,
- 0x01,0x04,0xad,0x4c,0x7b,0xad,0x20,0x8d,
- 0x01,0x04,0xad,0xb0,0xc6,0x44,0x20,0x8d,
- 0x01,0x04,0xad,0xd0,0x98,0xda,0x20,0x8d,
- 0x01,0x04,0xad,0xf1,0xe3,0xf3,0x20,0x8d,
- 0x01,0x04,0xad,0xf6,0x1b,0x07,0x20,0x8d,
- 0x01,0x04,0xad,0xff,0xf0,0xcd,0x20,0x8d,
- 0x01,0x04,0xae,0x1e,0x2f,0x0f,0x20,0x8d,
- 0x01,0x04,0xae,0x72,0xfa,0x56,0x20,0x8d,
- 0x01,0x04,0xae,0x8a,0x23,0xe5,0x20,0x8d,
- 0x01,0x04,0xae,0x8e,0xbf,0x88,0x20,0x8d,
- 0x01,0x04,0xb0,0x0a,0x8f,0xbe,0x20,0x8d,
+ 0x01,0x04,0xac,0x6f,0xb0,0xf4,0x20,0x8d,
+ 0x01,0x04,0xac,0xff,0x62,0x6c,0x20,0x8d,
+ 0x01,0x04,0xad,0x52,0x05,0xca,0x20,0x8d,
+ 0x01,0x04,0xad,0xb5,0x23,0x32,0x20,0x8d,
+ 0x01,0x04,0xad,0xd4,0xfd,0x89,0x20,0x8d,
+ 0x01,0x04,0xad,0xeb,0x49,0x57,0x20,0x8d,
+ 0x01,0x04,0xae,0x1e,0x1d,0x55,0x20,0x8d,
+ 0x01,0x04,0xae,0x8d,0xd1,0x28,0x20,0x8d,
+ 0x01,0x04,0xb0,0x09,0x11,0x79,0x20,0x8d,
+ 0x01,0x04,0xb0,0x0c,0x10,0x87,0x20,0x8d,
0x01,0x04,0xb0,0x4a,0x88,0xed,0x20,0x8d,
- 0x01,0x04,0xb0,0x76,0xdc,0x1d,0x20,0x8d,
- 0x01,0x04,0xb0,0x7e,0x74,0x07,0x20,0x8d,
+ 0x01,0x04,0xb0,0x4a,0x8b,0x78,0x20,0x8d,
+ 0x01,0x04,0xb0,0x7a,0x7a,0x86,0x20,0x8d,
0x01,0x04,0xb0,0x7e,0xa7,0x0a,0x20,0x8d,
+ 0x01,0x04,0xb0,0x97,0xf4,0x82,0x20,0x8d,
+ 0x01,0x04,0xb0,0xba,0x13,0x6a,0x20,0x8d,
0x01,0x04,0xb0,0xd4,0xb9,0x99,0x20,0x8d,
- 0x01,0x04,0xb0,0xeb,0xd1,0xba,0x20,0x8d,
- 0x01,0x04,0xb1,0x51,0xec,0x75,0x20,0x8d,
- 0x01,0x04,0xb1,0x59,0xcd,0x46,0x20,0x8d,
- 0x01,0x04,0xb2,0x30,0xa8,0x0c,0x20,0x8d,
+ 0x01,0x04,0xb1,0x8e,0x92,0xc1,0x20,0x8d,
+ 0x01,0x04,0xb2,0x15,0x76,0xb2,0x20,0x8d,
+ 0x01,0x04,0xb2,0x3d,0x8d,0xc6,0x20,0x8d,
0x01,0x04,0xb2,0x7c,0xa2,0xd1,0x20,0x8d,
+ 0x01,0x04,0xb2,0x8f,0x19,0xc2,0x20,0x8d,
+ 0x01,0x04,0xb2,0x9a,0xe9,0xc5,0x20,0x8d,
0x01,0x04,0xb2,0x9f,0x62,0x85,0x20,0x8d,
- 0x01,0x04,0xb2,0xc4,0x59,0xd1,0x20,0x8d,
+ 0x01,0x04,0xb2,0xe8,0xba,0xbf,0x20,0x8d,
0x01,0x04,0xb2,0xec,0x89,0x3f,0x20,0x8d,
- 0x01,0x04,0xb2,0xfc,0x7b,0x18,0x20,0x8d,
- 0x01,0x04,0xb3,0x2b,0xaa,0xba,0x20,0x8d,
- 0x01,0x04,0xb4,0x96,0x2e,0xbb,0x20,0x8d,
- 0x01,0x04,0xb5,0x75,0x80,0x8c,0x20,0x8d,
- 0x01,0x04,0xb8,0x13,0x13,0x10,0x20,0x8d,
- 0x01,0x04,0xb9,0x15,0xd9,0x30,0x20,0x8d,
+ 0x01,0x04,0xb3,0x3c,0x95,0x04,0x20,0x8d,
+ 0x01,0x04,0xb8,0xa0,0x6e,0x68,0x20,0x8d,
+ 0x01,0x04,0xb8,0xae,0x25,0x8b,0x20,0x8d,
+ 0x01,0x04,0xb9,0x08,0x68,0xb3,0x20,0x8d,
+ 0x01,0x04,0xb9,0x0e,0x1e,0x19,0x20,0x8d,
0x01,0x04,0xb9,0x19,0x30,0xb8,0x20,0x8d,
- 0x01,0x04,0xb9,0x1f,0x88,0xf6,0x20,0x8d,
0x01,0x04,0xb9,0x34,0x5d,0x2d,0x20,0x8d,
0x01,0x04,0xb9,0x40,0x74,0x0f,0x20,0x8d,
- 0x01,0x04,0xb9,0x44,0xf9,0x5b,0x20,0x8d,
+ 0x01,0x04,0xb9,0x45,0x69,0x75,0x20,0x8d,
0x01,0x04,0xb9,0x62,0x36,0x14,0x20,0x8d,
0x01,0x04,0xb9,0x6b,0x53,0x37,0x20,0x8d,
+ 0x01,0x04,0xb9,0x84,0x6d,0x7a,0x20,0x8d,
+ 0x01,0x04,0xb9,0x87,0x51,0x32,0x20,0x8d,
0x01,0x04,0xb9,0x8c,0xfd,0xa9,0x20,0x8d,
- 0x01,0x04,0xb9,0x94,0x91,0x4a,0x20,0x8d,
+ 0x01,0x04,0xb9,0x94,0x03,0xe3,0x20,0x8d,
+ 0x01,0x04,0xb9,0x9a,0x02,0x03,0x20,0x8d,
+ 0x01,0x04,0xb9,0xa2,0x5c,0x24,0x20,0x8d,
+ 0x01,0x04,0xb9,0xa3,0x2c,0x24,0x20,0x8d,
0x01,0x04,0xb9,0xa5,0xaa,0x13,0x20,0x8d,
0x01,0x04,0xb9,0xa7,0x71,0x3b,0x20,0x8d,
- 0x01,0x04,0xb9,0xb9,0x1a,0x8d,0x1f,0xaf,
- 0x01,0x04,0xb9,0xc5,0xa3,0x88,0x20,0x8d,
+ 0x01,0x04,0xb9,0xb9,0x3b,0x0c,0x20,0x8d,
+ 0x01,0x04,0xb9,0xcb,0x29,0x94,0x20,0x8d,
0x01,0x04,0xb9,0xd1,0x0c,0x4c,0x20,0x8d,
0x01,0x04,0xb9,0xd1,0x46,0x11,0x20,0x8d,
- 0x01,0x04,0xb9,0xe3,0x9c,0xe2,0x20,0x8d,
+ 0x01,0x04,0xb9,0xd2,0x7d,0x21,0x20,0x8d,
0x01,0x04,0xb9,0xe9,0xbd,0xd2,0x20,0x8d,
+ 0x01,0x04,0xb9,0xee,0x83,0x13,0x20,0x8d,
+ 0x01,0x04,0xb9,0xef,0xdc,0xd2,0x20,0x8d,
0x01,0x04,0xb9,0xef,0xdd,0x05,0x20,0x8d,
- 0x01,0x04,0xb9,0xf4,0x64,0x6a,0x20,0x8d,
- 0x01,0x04,0xb9,0xfe,0x61,0xa4,0x20,0x8d,
- 0x01,0x04,0xba,0x21,0xa7,0x0b,0x20,0x8d,
- 0x01,0x04,0xba,0xb0,0x62,0x25,0x20,0x8d,
+ 0x01,0x04,0xb9,0xfa,0x5a,0xf6,0x20,0x8d,
0x01,0x04,0xba,0xf9,0xd9,0x19,0x20,0x8d,
0x01,0x04,0xba,0xfa,0x5f,0x84,0x20,0x8d,
- 0x01,0x04,0xbc,0x20,0x0e,0x1f,0x20,0x8e,
0x01,0x04,0xbc,0x23,0xa7,0x0e,0x20,0x8d,
- 0x01,0x04,0xbc,0x44,0x2d,0x8f,0x20,0x8d,
- 0x01,0x04,0xbc,0x75,0xc8,0xd4,0x20,0x8d,
- 0x01,0x04,0xbc,0x8a,0x58,0x0e,0x20,0x8d,
- 0x01,0x04,0xbc,0x97,0xed,0x9e,0x20,0x8d,
- 0x01,0x04,0xbc,0x9a,0xec,0x31,0x20,0x8d,
- 0x01,0x04,0xbd,0x7b,0xb1,0x80,0x20,0x8d,
+ 0x01,0x04,0xbc,0x44,0x35,0x2c,0x20,0x8d,
+ 0x01,0x04,0xbc,0x78,0xff,0x73,0x20,0x8d,
+ 0x01,0x04,0xbd,0x06,0xc3,0x6f,0x20,0x8d,
+ 0x01,0x04,0xbe,0x02,0x82,0x2c,0x20,0x8d,
+ 0x01,0x04,0xbe,0x0d,0x7a,0x59,0x20,0x8d,
0x01,0x04,0xbe,0x7b,0x1b,0x0b,0x20,0x8d,
0x01,0x04,0xbe,0x91,0x7f,0xfe,0x20,0x8d,
- 0x01,0x04,0xc0,0x45,0x35,0x4d,0x20,0x8d,
+ 0x01,0x04,0xbf,0xdc,0x9c,0x40,0x20,0x8d,
+ 0x01,0x04,0xc0,0x1f,0x88,0x5a,0x20,0x8d,
+ 0x01,0x04,0xc0,0x45,0x35,0x2b,0x20,0x8d,
0x01,0x04,0xc0,0x92,0x89,0x2c,0x20,0x8d,
- 0x01,0x04,0xc0,0xde,0x18,0x36,0x20,0x8d,
- 0x01,0x04,0xc0,0xde,0x93,0x8d,0x20,0x8d,
- 0x01,0x04,0xc1,0x20,0x7f,0xa2,0xee,0x29,
- 0x01,0x04,0xc1,0x6f,0xc6,0xbb,0x1f,0xaf,
- 0x01,0x04,0xc1,0xc4,0x25,0x3e,0x20,0x8d,
- 0x01,0x04,0xc2,0x0d,0x50,0xb9,0x3c,0x46,
- 0x01,0x04,0xc2,0x93,0x71,0xc9,0x20,0x8d,
- 0x01,0x04,0xc2,0xa5,0x1e,0x14,0x20,0x8d,
- 0x01,0x04,0xc2,0xbf,0xef,0x62,0x20,0x8d,
- 0x01,0x04,0xc3,0x38,0x3f,0x04,0x20,0x8d,
- 0x01,0x04,0xc3,0x38,0x3f,0x0a,0x20,0x8d,
- 0x01,0x04,0xc3,0x7b,0xef,0xb9,0x20,0x8d,
- 0x01,0x04,0xc3,0x8c,0xe2,0x9a,0x20,0x8d,
- 0x01,0x04,0xc6,0x01,0xe7,0x06,0x20,0x8d,
- 0x01,0x04,0xc6,0x94,0x70,0x1b,0x20,0x8d,
- 0x01,0x04,0xc7,0x7e,0xea,0xed,0x20,0x8d,
- 0x01,0x04,0xc7,0xc1,0xae,0xad,0x20,0x8d,
+ 0x01,0x04,0xc0,0xae,0x79,0x21,0x20,0x8d,
+ 0x01,0x04,0xc0,0xde,0x93,0xaf,0x20,0x8d,
+ 0x01,0x04,0xc1,0xc6,0x22,0x18,0x20,0x8d,
+ 0x01,0x04,0xc1,0xde,0x82,0x0e,0x20,0x8d,
+ 0x01,0x04,0xc2,0x23,0xb9,0xa7,0x20,0x8d,
+ 0x01,0x04,0xc2,0x36,0x53,0xea,0x20,0x8d,
+ 0x01,0x04,0xc2,0xe9,0x54,0x64,0x20,0x8d,
+ 0x01,0x04,0xc3,0x02,0x49,0x58,0x20,0x8d,
+ 0x01,0x04,0xc3,0x30,0x0c,0x08,0x20,0x8d,
+ 0x01,0x04,0xc3,0x9a,0xc8,0x9d,0x20,0x8d,
+ 0x01,0x04,0xc5,0xd3,0x85,0x0f,0x20,0x8d,
+ 0x01,0x04,0xc6,0x54,0x92,0x08,0x20,0x8d,
+ 0x01,0x04,0xc6,0x62,0x37,0x56,0x20,0x8d,
0x01,0x04,0xc7,0xf7,0x07,0xd0,0x20,0x8d,
- 0x01,0x04,0xc8,0x7a,0xb5,0x2e,0x20,0x8d,
+ 0x01,0x04,0xc8,0x74,0x9a,0x83,0x20,0x8d,
0x01,0x04,0xc9,0xbf,0x06,0x67,0x20,0x8d,
- 0x01,0x04,0xc9,0xd4,0x24,0xd1,0x20,0x8d,
0x01,0x04,0xc9,0xdd,0xea,0xc8,0x20,0x8d,
+ 0x01,0x04,0xca,0x2f,0xe1,0xf2,0x20,0x8d,
+ 0x01,0x04,0xca,0x6b,0xdb,0x82,0x20,0x8d,
0x01,0x04,0xca,0x6c,0xd3,0x87,0x20,0x8d,
- 0x01,0x04,0xca,0xa9,0x11,0xb2,0x20,0x8d,
- 0x01,0x04,0xca,0xb1,0x18,0x8c,0x20,0x8d,
- 0x01,0x04,0xcb,0x82,0x30,0x75,0x22,0xb5,
- 0x01,0x04,0xcb,0x84,0x5e,0xc4,0x20,0x8d,
+ 0x01,0x04,0xca,0x8a,0x0d,0x7a,0x20,0x8d,
+ 0x01,0x04,0xcb,0x56,0xc3,0x20,0x20,0x8d,
+ 0x01,0x04,0xcb,0xb8,0x34,0xf7,0x20,0x8d,
+ 0x01,0x04,0xcc,0x6f,0xa3,0x72,0x20,0x8d,
0x01,0x04,0xcd,0xb2,0x29,0x7c,0x20,0x8d,
- 0x01,0x04,0xce,0x48,0xc9,0xe4,0x20,0x8d,
0x01,0x04,0xce,0xc0,0xcb,0x00,0x20,0x8d,
- 0x01,0x04,0xce,0xdf,0x99,0x34,0x20,0x8d,
- 0x01,0x04,0xcf,0x86,0xd8,0x91,0x20,0x8e,
- 0x01,0x04,0xcf,0xbc,0x9a,0x32,0x20,0x8d,
0x01,0x04,0xcf,0xe5,0x2e,0x50,0x20,0x8d,
+ 0x01,0x04,0xcf,0xf4,0xf8,0x51,0x20,0x8d,
0x01,0x04,0xcf,0xff,0xc1,0x2f,0x20,0x8d,
- 0x01,0x04,0xd0,0x68,0x5c,0x4a,0x20,0x8d,
+ 0x01,0x04,0xd0,0x3b,0x85,0x3f,0x20,0x8d,
0x01,0x04,0xd1,0x3a,0x91,0x9d,0x20,0x8d,
- 0x01,0x04,0xd1,0x3a,0x9e,0xe8,0x20,0x8f,
- 0x01,0x04,0xd1,0x8d,0x2b,0xf3,0x20,0x8d,
- 0x01,0x04,0xd1,0xe2,0x8e,0x3e,0x20,0x8d,
- 0x01,0x04,0xd1,0xed,0x7f,0xe3,0x20,0x8d,
+ 0x01,0x04,0xd1,0x61,0xbd,0xf9,0x20,0x8d,
+ 0x01,0x04,0xd1,0xb1,0x8a,0xf5,0x20,0x8d,
0x01,0x04,0xd1,0xed,0x85,0x36,0x20,0x8d,
- 0x01,0x04,0xd3,0xf8,0x5a,0x32,0x20,0x8d,
- 0x01,0x04,0xd4,0x15,0x12,0x4e,0x20,0x8d,
+ 0x01,0x04,0xd2,0x36,0x25,0xbe,0x20,0x8d,
+ 0x01,0x04,0xd2,0x36,0x27,0xee,0x20,0x8d,
0x01,0x04,0xd4,0x22,0xe1,0x76,0x20,0x8d,
- 0x01,0x04,0xd4,0x33,0x92,0x89,0x20,0x8d,
- 0x01,0x04,0xd4,0xe3,0xd3,0x57,0x20,0x8d,
- 0x01,0x04,0xd5,0x00,0x45,0x4c,0x20,0x8d,
- 0x01,0x04,0xd5,0x05,0x24,0x3a,0x20,0x8d,
+ 0x01,0x04,0xd4,0x29,0x09,0x1e,0x20,0x8d,
+ 0x01,0x04,0xd4,0x33,0x84,0xb0,0x20,0x8d,
+ 0x01,0x04,0xd4,0x45,0x3c,0x4d,0x20,0x8d,
+ 0x01,0x04,0xd4,0x56,0x20,0x6a,0x20,0x8d,
0x01,0x04,0xd5,0x2f,0x40,0x69,0x20,0x8d,
- 0x01,0x04,0xd5,0x59,0x87,0x97,0x20,0x8d,
0x01,0x04,0xd5,0x8d,0x9a,0xc9,0x20,0x8d,
- 0x01,0x04,0xd5,0x9f,0xc6,0x2d,0x20,0x8d,
+ 0x01,0x04,0xd5,0x8e,0x94,0xa9,0x20,0x8d,
0x01,0x04,0xd5,0xb8,0xf4,0x18,0x20,0x8d,
- 0x01,0x04,0xd5,0xd6,0x42,0xb6,0x20,0x8d,
- 0x01,0x04,0xd5,0xe2,0x7b,0x4c,0x20,0x8d,
+ 0x01,0x04,0xd5,0xe3,0x93,0xf4,0x20,0x8d,
+ 0x01,0x04,0xd5,0xfa,0x15,0x70,0x20,0x8d,
0x01,0x04,0xd8,0x92,0xfb,0x08,0x20,0x8d,
- 0x01,0x04,0xd8,0xba,0xee,0x0e,0x20,0x8d,
- 0x01,0x04,0xd9,0x05,0x96,0x72,0x20,0x8d,
+ 0x01,0x04,0xd8,0xe8,0x9d,0x68,0x20,0x8d,
0x01,0x04,0xd9,0x0f,0xb2,0x0b,0x20,0x8d,
- 0x01,0x04,0xd9,0x18,0xef,0x6d,0x20,0x8d,
- 0x01,0x04,0xd9,0x40,0x2f,0x8a,0x20,0x8d,
- 0x01,0x04,0xd9,0x49,0x50,0x68,0x20,0x8d,
- 0x01,0x04,0xd9,0x4f,0xb5,0x26,0x20,0x8d,
+ 0x01,0x04,0xd9,0x1a,0x20,0x0a,0x20,0x8d,
+ 0x01,0x04,0xd9,0x40,0x2f,0xc8,0x20,0x8d,
+ 0x01,0x04,0xd9,0x4c,0x33,0x19,0x20,0x8d,
0x01,0x04,0xd9,0x5c,0x37,0xf6,0x20,0x8d,
- 0x01,0x04,0xd9,0x71,0x79,0xa9,0x20,0x8d,
- 0x01,0x04,0xd9,0x73,0x74,0xfa,0x20,0x8d,
- 0x01,0x04,0xd9,0x9b,0xf4,0xaa,0x20,0x8d,
0x01,0x04,0xd9,0xaa,0x7c,0xaa,0x20,0x8d,
- 0x01,0x04,0xdc,0x84,0x87,0x36,0x20,0x8d,
- 0x01,0x04,0xdc,0xe9,0xb2,0xc7,0x20,0x8d,
- 0x01,0x04,0xde,0x9a,0x6f,0x2e,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x16,0x20,0x05,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x19,0xf0,0x60,0x01,0x39,0xaa,0x54,0x00,0x03,0xff,0xfe,0xf0,0x09,0x16,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x19,0xf0,0x80,0x01,0x0f,0x71,0x54,0x00,0x04,0xff,0xfe,0x10,0x6a,0x63,0x20,0x8d,
+ 0x01,0x04,0xd9,0xb4,0xdd,0xa2,0x20,0x8d,
+ 0x01,0x04,0xd9,0xb4,0xee,0x89,0x20,0x8d,
+ 0x01,0x04,0xdc,0x54,0xe8,0x2e,0x20,0x8d,
+ 0x01,0x04,0xdc,0x85,0x27,0x3d,0x20,0x8d,
+ 0x01,0x04,0xdc,0xe9,0x5b,0xb6,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x10,0x00,0x1d,0xb3,0x54,0x00,0x04,0xff,0xfe,0x56,0x5a,0x8d,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x00,0x05,0x24,0xda,0x3e,0xec,0xef,0xff,0xfe,0xb9,0xf3,0x6e,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x00,0x05,0x24,0xda,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x00,0x05,0x45,0x35,0x3e,0xec,0xef,0xff,0xfe,0xb9,0x87,0xe4,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x00,0x05,0x45,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x20,0x01,0x1b,0xc0,0x00,0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x1c,0x02,0x01,0x1e,0x35,0x00,0xdf,0x25,0x63,0x21,0x82,0x60,0xd9,0xbe,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x41,0xd0,0x10,0x04,0x1b,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x93,
+ 0x02,0x10,0x20,0x01,0x1c,0x04,0x40,0x08,0x63,0x00,0x8a,0x5f,0x26,0x78,0x11,0x4b,0xa6,0x60,0x20,0x8d,
0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0x37,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0xaa,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0x8f,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0xbb,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x02,0xbf,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x41,0xd0,0x03,0x03,0x65,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x41,0xd0,0x06,0x02,0x44,0x93,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x08,0xb9,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x03,0x03,0xde,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x04,0x03,0x3d,0x61,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x04,0x05,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x08,0xed,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x0a,0x69,0xa2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x01,0x41,0xf0,0x00,0x00,0x00,0x00,0x00,0x62,0x69,0x74,0x63,0x6f,0x69,0x6e,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x44,0xb8,0x02,0x56,0x5d,0x11,0x02,0x16,0x3e,0xff,0xfe,0x39,0xd5,0xd4,0x20,0x8d,
0x02,0x10,0x20,0x01,0x04,0x70,0x1b,0x62,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x07,0x08,0x03,0x02,0x0c,0x29,0xff,0xfe,0x2d,0x58,0x79,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x05,0x04,0x3b,0x28,0x31,0x85,0x30,0x71,0x79,0x58,0x64,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x09,0x0b,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x20,0x8d,
0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x15,0x01,0x06,0xe2,0xd5,0x5e,0xff,0xfe,0x42,0x7a,0xe5,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x15,0x0c,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x04,0x70,0x00,0x26,0x04,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x7c,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x1b,0x03,0x65,0xaa,0x20,0x66,0xff,0xfe,0x3f,0x19,0x09,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x1b,0x05,0xa6,0x02,0x16,0x3e,0xff,0xfe,0x24,0x11,0x62,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x6a,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x20,0x01,0x04,0x70,0x75,0xe9,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x04,0x70,0xde,0x5a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x24,0x75,
- 0x02,0x10,0x20,0x01,0x4b,0xa0,0xba,0xbe,0x05,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x8c,0xa0,0x00,0x02,0x4e,0x72,0xb9,0xff,0xfe,0x56,0xf8,0xb8,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0xdb,0xc7,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x00,0x01,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4b,0xa0,0xca,0xfe,0x14,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4b,0xa0,0xff,0xff,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x30,0xb7,0x1d,0x7b,0x6f,0xec,0x4c,0x5c,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x08,0x8e,0xb4,0xff,0x2a,0xd0,0x69,0x9b,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x9c,0x1c,0xcc,0x31,0x9f,0xe8,0x55,0x05,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0xa0,0xc4,0xd4,0x1f,0x04,0xc4,0x1b,0xb0,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0xfd,0x76,0xc1,0xd3,0x18,0x54,0x5b,0xd9,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x01,0x00,0x00,0x00,0x00,0x76,0x76,0x80,0x90,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x01,0xb9,0x77,0xbd,0x71,0x46,0x12,0x8e,0x40,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0xaf,0x0e,0x35,0x64,0x00,0x00,0x00,0x00,0x00,0x69,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0xaf,0x0e,0x35,0x64,0x00,0x00,0x00,0x00,0x00,0x69,0x00,0x90,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x4d,0xe8,0xb1,0xb2,0x00,0x01,0x00,0x00,0xde,0xad,0xbe,0xef,0x00,0x07,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x05,0x60,0x44,0x1f,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x20,0x8d,
0x02,0x10,0x20,0x01,0x06,0x38,0xa0,0x00,0x41,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0x01,0x91,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x06,0x78,0x0a,0xcc,0x00,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x25,0xdc,0x00,0x91,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x20,0x01,0x06,0x7c,0x26,0xb4,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x20,0x8d,
0x02,0x10,0x20,0x01,0x06,0x7c,0x2d,0xb8,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x07,0xc0,0x23,0x10,0x00,0x00,0xf8,0x16,0x3e,0xff,0xfe,0x0d,0x4a,0xb6,0x20,0x8d,
0x02,0x10,0x20,0x01,0x07,0xc0,0x23,0x10,0x00,0x00,0xf8,0x16,0x3e,0xff,0xfe,0x6c,0x4f,0x58,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x08,0x61,0x32,0x46,0x0a,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x0b,0x07,0x02,0xe6,0x38,0xd7,0xba,0x27,0xeb,0xff,0xfe,0x60,0x3d,0xc1,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x08,0x61,0x32,0x42,0x84,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x08,0xb0,0x13,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0xb0,0x30,0x24,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x02,0xef,0x6e,0x4a,0x00,0x3d,0x97,0x4e,0x78,0x4a,0x68,0x4b,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x5d,0x32,0xb1,0x42,0x8f,0x77,0x3c,0x7d,0xa2,0xfd,0xed,0x2e,0x20,0x8d,
0x02,0x10,0x20,0x01,0x0b,0x07,0x64,0x61,0x78,0x11,0x04,0x89,0xd2,0xda,0x0e,0x07,0x1a,0xf7,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x0b,0x07,0x0a,0xc9,0x44,0x2b,0x79,0xd6,0xbb,0xbe,0xb3,0x7c,0xa7,0x83,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x64,0x6b,0x80,0x74,0x32,0xe8,0x92,0x43,0xa3,0x37,0xe6,0x0a,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x64,0x6b,0x80,0x74,0x4c,0xc6,0x79,0xa5,0x3a,0xf7,0x71,0x32,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x0a,0xd4,0xca,0x4b,0x7d,0xd5,0x84,0x71,0x50,0xc3,0x53,0x63,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x12,0x01,0x07,0x1a,0x2e,0x59,0xe5,0xff,0xfe,0x42,0x52,0xf4,0x20,0x8d,
0x02,0x10,0x20,0x01,0x0b,0xc8,0x16,0x00,0x00,0x00,0x02,0x08,0xa2,0xff,0xfe,0x0c,0x8a,0x2e,0x20,0x8d,
0x02,0x10,0x20,0x01,0x0b,0xc8,0x32,0x3c,0x00,0xff,0xa6,0x34,0x38,0x4f,0x18,0x49,0xf4,0xbc,0x20,0x8d,
0x02,0x10,0x20,0x01,0x0b,0xc8,0x32,0x3c,0x00,0xff,0xd2,0x17,0xc2,0xff,0xfe,0x07,0x2c,0xd9,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x0b,0xc8,0x3b,0xec,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x20,0x02,0x2f,0x5b,0xa5,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x2f,0x5b,0xa5,0xf9,0x22,0xb5,
- 0x02,0x10,0x20,0x03,0x00,0xcb,0x87,0x13,0x61,0x02,0xaa,0xa1,0x59,0xff,0xfe,0x57,0x77,0x79,0x20,0x8d,
- 0x02,0x10,0x20,0x03,0x00,0xe0,0x37,0x0e,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x20,0x8d,
- 0x02,0x10,0x20,0x03,0x00,0xf6,0x3f,0x10,0x67,0x00,0x4c,0x9f,0x76,0x20,0x83,0x24,0xd4,0xa7,0x20,0x8d,
- 0x02,0x10,0x24,0x00,0x24,0x10,0xce,0xa2,0x0d,0x00,0x41,0xbc,0xc9,0xea,0x86,0x1b,0x51,0xee,0x20,0x8d,
- 0x02,0x10,0x24,0x00,0x24,0x11,0xa3,0xe1,0x49,0x00,0x25,0x68,0x68,0x4b,0x0e,0x99,0x71,0x20,0x20,0x8d,
- 0x02,0x10,0x24,0x00,0x24,0x11,0xa3,0xe1,0x49,0x00,0x29,0x87,0xb8,0x8f,0x61,0xe0,0x84,0xfa,0x20,0x8d,
- 0x02,0x10,0x24,0x00,0x3b,0x00,0x00,0x20,0x00,0x0c,0xba,0xcb,0x29,0xff,0xfe,0xab,0x88,0x86,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x07,0x00,0x2b,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x07,0x00,0x8d,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0e,0x68,0x54,0x00,0x58,0xd0,0xbd,0x15,0xea,0x8c,0x5b,0x20,0x75,0x23,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x24,0x11,0xa3,0xe1,0x49,0x00,0x72,0x98,0xf5,0x50,0x67,0xe7,0xb9,0x9b,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x89,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x2b,0x5c,0x0b,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x89,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x5a,0x68,0x5c,0x20,0x8d,
0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x10,0x20,0x8d,
0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x20,0x20,0x8d,
- 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x01,0x00,0x20,0x8d,
- 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x01,0x30,0x20,0x8d,
0x02,0x10,0x24,0x01,0xd0,0x02,0x39,0x02,0x07,0x00,0xd7,0x2c,0x5e,0x22,0x4e,0x95,0x38,0x9d,0x20,0x8d,
- 0x02,0x10,0x24,0x04,0x44,0x08,0x67,0x52,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x99,0x20,0x8d,
- 0x02,0x10,0x24,0x04,0x7a,0x85,0x41,0x61,0x2b,0x00,0x49,0xa1,0x42,0x7a,0x0f,0xac,0x34,0x09,0x20,0x8d,
- 0x02,0x10,0x24,0x05,0x98,0x00,0xb9,0x72,0xab,0x58,0x0c,0x05,0xe9,0x38,0x26,0x7e,0x02,0x71,0x20,0x8d,
+ 0x02,0x10,0x24,0x04,0x44,0x08,0x63,0xa4,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x50,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0x34,0x00,0x02,0x16,0x8b,0x00,0x02,0x11,0x32,0xff,0xfe,0xca,0x33,0x6b,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0x8c,0x00,0x00,0x00,0x34,0x22,0x01,0x33,0x00,0x18,0x02,0x28,0x01,0x08,0x20,0x8d,
0x02,0x10,0x24,0x06,0xda,0x11,0x01,0x69,0x0b,0x03,0x32,0xb5,0xf9,0x01,0x9f,0x7c,0x3e,0x4b,0x20,0x8d,
- 0x02,0x10,0x24,0x06,0xda,0x14,0x03,0x35,0xb6,0x01,0xce,0xb7,0xb4,0xfc,0xa8,0x55,0xf3,0xa5,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x18,0x09,0xf1,0xf3,0x01,0x7d,0x2e,0xc2,0x56,0xc1,0x12,0xf2,0xbe,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x18,0x09,0xf1,0xf3,0x03,0xc1,0xc9,0xc5,0x69,0xb7,0x99,0x20,0x57,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x1e,0x0a,0x4e,0x8a,0x00,0x20,0xdb,0xdd,0x8d,0x36,0x70,0x28,0xf0,0x20,0x8d,
0x02,0x10,0x24,0x06,0xda,0x1e,0x0a,0x4e,0x8a,0x03,0x2a,0xad,0x49,0x6b,0x76,0x8d,0xe4,0x97,0x20,0x8d,
- 0x02,0x10,0x24,0x07,0x88,0x00,0xbc,0x61,0x22,0x02,0xa0,0xc6,0x01,0x07,0x50,0x2b,0x4e,0x3b,0x20,0x8d,
- 0x02,0x10,0x24,0x09,0x00,0x10,0xca,0x20,0x1d,0xf0,0x02,0x24,0xe8,0xff,0xfe,0x1f,0x60,0xd9,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x17,0x00,0x22,0xf1,0x64,0x1f,0x00,0xe8,0x39,0xc8,0xeb,0x1d,0xa1,0xeb,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x17,0x00,0x9c,0x5d,0x0e,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x17,0x00,0x9c,0x5d,0x0e,0xd0,0xd0,0xd6,0x01,0xd9,0x5c,0xc2,0xab,0x47,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x17,0x02,0x1c,0xe0,0x40,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x1f,0x14,0x04,0x0e,0xe3,0x01,0xd1,0x55,0xaa,0x3a,0x77,0xbe,0x96,0x0e,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x1f,0x16,0x0a,0x08,0xb9,0x01,0x1a,0xfa,0xef,0x4e,0x4c,0xe7,0x2b,0xa4,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x1f,0x1c,0x02,0xd3,0x24,0x03,0x5b,0xac,0x3f,0xc6,0x65,0x13,0x7a,0x63,0x20,0x8d,
+ 0x02,0x10,0x24,0x07,0x36,0x40,0x21,0x07,0x12,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x24,0x07,0x36,0x40,0x30,0x10,0x40,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x24,0x07,0x88,0x00,0xbc,0x61,0x22,0x02,0xd6,0x3d,0x7e,0xff,0xfe,0x6c,0xdc,0x36,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x00,0x5c,0x5b,0x00,0xb0,0xaa,0xa1,0x59,0xff,0xfe,0x5f,0x61,0x5a,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x00,0xec,0x7b,0x57,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x19,0x00,0x40,0x00,0x4c,0xc4,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x19,0x00,0x40,0x00,0x4c,0xc4,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x19,0x00,0x40,0x00,0x4c,0xc4,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x19,0x00,0x40,0x00,0x4c,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x19,0x00,0x40,0x30,0xa2,0x5e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x1f,0x14,0x04,0x0e,0xe3,0x01,0xaf,0xdd,0xad,0x00,0xe5,0x68,0xd2,0x20,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x1f,0x1c,0x02,0xd3,0x24,0x00,0xf1,0x5e,0x2f,0x2a,0x76,0x0d,0xa3,0x3d,0x20,0x8d,
0x02,0x10,0x26,0x00,0x21,0x04,0x10,0x03,0xc5,0xab,0xdc,0x5e,0x90,0xff,0xfe,0x18,0x1d,0x08,0x20,0x8d,
0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0x92,0x27,0x45,0x20,0x8d,
0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0xcf,0x61,0xb6,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0xb3,0x01,0xb6,0x20,0x8d,
0x02,0x10,0x26,0x00,0x3c,0x00,0xe0,0x02,0x2e,0x32,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x14,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x2a,0x52,0x66,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x74,0x5f,0x59,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0xe6,0x21,0x46,0x20,0x8d,
0x02,0x10,0x26,0x00,0x3c,0x02,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0x5d,0x09,0xfb,0x20,0x8d,
- 0x02,0x10,0x26,0x00,0x40,0x40,0x28,0x54,0x5e,0x00,0xc6,0xe9,0x84,0xff,0xfe,0x46,0x0e,0xe8,0x21,0xda,
- 0x02,0x10,0x26,0x00,0x6c,0x54,0x71,0x00,0x1a,0xd1,0xbd,0xdf,0x55,0x0e,0x91,0xbe,0xf9,0xe1,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x40,0x40,0x20,0x04,0x32,0x01,0x45,0x9f,0x8f,0xe8,0x44,0x4d,0xba,0xf1,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x40,0x40,0x45,0x41,0x49,0x00,0x04,0xe1,0xb5,0x8a,0x84,0x38,0x45,0x0e,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x6c,0x54,0x71,0x00,0x1a,0xd1,0xc9,0x2e,0x03,0x6d,0x06,0x51,0xbd,0x18,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x88,0x01,0x2f,0x80,0x04,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x1c,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x88,0x01,0x8d,0x00,0x3e,0xb0,0x02,0x0c,0x29,0xff,0xfe,0xc3,0xd7,0x99,0x20,0x8d,
0x02,0x10,0x26,0x00,0x88,0x05,0x24,0x00,0x01,0x4e,0x12,0xdd,0xb1,0xff,0xfe,0xf2,0x30,0x13,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x01,0x84,0x03,0x00,0x0b,0xde,0x3c,0x29,0x8e,0x94,0x1b,0xa8,0xfd,0xe3,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x01,0x8c,0x80,0x80,0x30,0x0f,0x02,0x19,0xd1,0xff,0xfe,0x75,0xdc,0x2f,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x01,0x8d,0x46,0x00,0x43,0xf1,0x20,0xe7,0xb3,0xff,0xfe,0xcf,0x0a,0x99,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x01,0x8d,0x87,0x01,0xc2,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x02,0x46,0x4d,0x7f,0x9e,0x28,0xf3,0x21,0x36,0xca,0x7a,0x71,0xc6,0x87,0x20,0x8d,
- 0x02,0x10,0x26,0x01,0x06,0x40,0xc2,0x01,0x96,0x0d,0x86,0xeb,0xf2,0x7d,0x66,0xa2,0xf2,0xc1,0x20,0x8d,
- 0x02,0x10,0x26,0x02,0x02,0x41,0x75,0xd1,0x2b,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x20,0x8d,
- 0x02,0x10,0x26,0x02,0xff,0xb8,0x00,0x00,0x00,0x00,0x02,0x08,0x00,0x72,0x00,0x57,0x02,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x01,0x84,0x03,0x00,0x15,0x6c,0xba,0x4c,0x00,0x30,0x09,0xda,0x6c,0x06,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x03,0x46,0x0d,0x7f,0xff,0xf7,0x18,0xc6,0x48,0x56,0xef,0x75,0x74,0x4c,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x04,0x05,0x4a,0x00,0x08,0x76,0xc8,0xd3,0xf0,0x81,0x2c,0xe8,0xba,0x8e,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0x02,0x4c,0x0b,0x8f,0xcd,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0xfe,0xc3,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0xff,0x16,0x00,0x01,0x00,0x00,0x00,0x01,0x04,0x12,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x01,0x26,0x18,0xc0,0x00,0x2e,0xc1,0xdf,0x1f,0xa4,0x63,0x91,0x19,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x03,0x01,0x1b,0xe1,0x00,0x02,0x0c,0x29,0xff,0xfe,0x38,0xbb,0xc0,0x20,0x8d,
0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x85,0x1f,0x58,0x4d,0x7a,0xba,0xaf,0xfb,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x02,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x30,0x04,0x07,0x0d,0x14,0x00,0x85,0x32,0x29,0x00,0xce,0x6f,0xac,0xdf,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x30,0x04,0x07,0x45,0x09,0x00,0xf0,0xd7,0x55,0x6a,0x0a,0x8c,0xce,0xd5,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x60,0x80,0xc0,0x00,0x5d,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x4f,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x80,0x00,0xd1,0x00,0x89,0x91,0xcc,0x29,0xcc,0xff,0xfe,0x42,0x30,0x0c,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7b,0xba,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0x67,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x24,0x16,0x06,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x29,0xec,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x24,0x18,0xee,0x80,0x00,0x02,0x0e,0xc4,0xff,0xfe,0xd1,0xef,0x15,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x60,0x00,0xa4,0x00,0x93,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x60,0x10,0x70,0x01,0x48,0x30,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x01,0x20,0x8d,
0x02,0x10,0x26,0x03,0x80,0x80,0x1f,0x07,0x6f,0xdd,0x7d,0xe2,0xd9,0x69,0x78,0xc9,0xb7,0xea,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x80,0x80,0x73,0x00,0x05,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xea,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x80,0xa0,0x07,0x03,0x40,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x01,0x80,0x00,0xf3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x18,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x3d,0x08,0x00,0x00,0x00,0x05,0xd9,0x41,0x4b,0x03,0xa0,0x93,0x13,0x1b,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x7c,0x00,0x01,0x20,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,0xeb,0x24,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x0a,0x00,0x00,0x21,0x30,0x43,0xbf,0x6a,0x53,0x5e,0xdf,0xeb,0x5b,0x7b,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x1c,0xe7,0x40,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x1d,0x44,0xe0,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0x80,0xd6,0x00,0x18,0x00,0x7c,0xe1,0x74,0xa2,0x6a,0x8a,0x46,0x43,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0x81,0x6c,0x00,0x30,0x6e,0x02,0x15,0x5d,0xff,0xfe,0x02,0x15,0x0a,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x3d,0x09,0x71,0x82,0x87,0x00,0xbb,0xa9,0xcd,0xe6,0x5b,0x37,0xa8,0xdf,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x40,0x80,0x10,0x36,0x80,0xb1,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xbe,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x0a,0x00,0x00,0x03,0x12,0x23,0x02,0x16,0x3e,0xff,0xfe,0x27,0x76,0xe0,0x20,0x8d,
0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x26,0x1f,0x60,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x07,0xe2,0xe0,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0x14,0x30,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x01,0x3e,0xf0,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x01,0x7a,0x70,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0xc1,0x30,0x00,0x20,0x8d,
0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0xe5,0xb0,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0x4a,0x80,0xa3,0x02,0x79,0x40,0x72,0x54,0x1e,0xd4,0x90,0xd7,0x4f,0x39,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0x4a,0x80,0xa3,0x02,0x79,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x26,0x05,0x64,0x00,0x00,0x30,0xf2,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x26,0x05,0x6f,0x80,0x00,0x00,0x00,0x07,0xfc,0x1b,0xcc,0xff,0xfe,0x8a,0xd8,0x22,0x20,0x8d,
- 0x02,0x10,0x26,0x05,0xa1,0x40,0x20,0x76,0x82,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x05,0xa1,0x40,0x30,0x07,0x12,0x87,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0xa1,0x40,0x30,0x10,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x26,0x05,0xae,0x00,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0x0b,0x40,0x14,0xd0,0x5b,0x00,0x79,0x88,0x0e,0xb8,0x6b,0xb6,0x66,0xe2,0x20,0x8d,
0x02,0x10,0x26,0x05,0xc0,0x00,0x2a,0x0a,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0x1a,0x00,0x00,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x11,0x7c,0x4d,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0x53,0x00,0x02,0x03,0x12,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x53,0x00,0x00,0x61,0x08,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x14,0x25,0xb5,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x21,0x9c,0x2f,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x21,0xbf,0x32,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x33,0x4d,0x1b,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x3d,0x04,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0xf2,0xc0,0xe1,0xc2,0x00,0x69,0x12,0xc3,0x7b,0xff,0xfe,0x4d,0x94,0x31,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0xf2,0xc0,0xe1,0xc2,0x00,0x69,0xec,0xb2,0x6e,0x88,0x9f,0x33,0x50,0x57,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x00,0x06,0x20,0x03,0x01,0x05,0x02,0xd8,0x61,0xff,0xfe,0x0f,0x08,0x53,0x20,0x8d,
0x02,0x10,0x26,0x20,0x00,0x6e,0xa0,0x00,0x00,0x01,0x00,0x42,0x00,0x42,0x00,0x42,0x00,0x42,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x03,0xd5,0x70,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x2a,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x31,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0c,0xe6,0x34,0x20,0x8d,
- 0x02,0x10,0x28,0x00,0x00,0x40,0x00,0x33,0x08,0xab,0xa0,0xe7,0xb2,0x15,0xfc,0x83,0x5c,0x31,0x20,0x8d,
- 0x02,0x10,0x28,0x00,0x0b,0xf0,0x01,0x49,0x0f,0x4b,0xf8,0xdf,0x8d,0x7d,0x80,0x1b,0xe2,0x5e,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x01,0x4c,0x01,0x98,0x80,0xd5,0x76,0x03,0x41,0xd1,0xd3,0xfc,0xe7,0x97,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x01,0x4d,0xae,0x81,0x82,0x7b,0x99,0xa8,0x1e,0x3f,0x6d,0xb2,0x29,0xdb,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x0d,0x57,0x55,0x37,0x48,0x00,0x3e,0x7c,0x3f,0xff,0xfe,0x7b,0x80,0xaa,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x12,0xe0,0x01,0x01,0x00,0x99,0x02,0x0c,0x29,0xff,0xfe,0x29,0xd0,0x3f,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x13,0x28,0xe1,0x01,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x63,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x05,0x16,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x09,0x90,0x0b,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x0b,0x30,0x0e,0x20,0x8d,
+ 0x02,0x10,0x28,0x00,0x01,0x50,0x01,0x1d,0x0d,0x2f,0xbd,0xac,0x78,0x07,0x02,0xf5,0x4a,0xa0,0x20,0x8d,
+ 0x02,0x10,0x28,0x03,0x98,0x00,0xa0,0x07,0x82,0xba,0x65,0x0b,0x82,0xb8,0x83,0x77,0x00,0xd0,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4c,0x01,0x55,0x45,0xe0,0x1e,0x86,0x15,0xa3,0xef,0xd9,0x72,0x87,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4c,0x65,0x7d,0x40,0x30,0x28,0xb4,0x0e,0xff,0xfe,0x9b,0x88,0x94,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4d,0x10,0x87,0x94,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x02,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x09,0x54,0x00,0x24,0x00,0x02,0xb3,0x90,0xd8,0x3b,0x35,0x8a,0xdb,0x53,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x0d,0x57,0x55,0x4d,0xde,0x00,0x3e,0x7c,0x3f,0xff,0xfe,0x7b,0x80,0xaa,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x10,0x28,0x83,0x8c,0x56,0x3a,0xfd,0x25,0x87,0xb6,0x5a,0x54,0x08,0x11,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x12,0x98,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x65,0x42,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x02,0x15,0x5d,0xff,0xfe,0xd6,0x10,0x33,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xbc,0x03,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x16,0x30,0x00,0x10,0x10,0x03,0x00,0x00,0x0b,0x19,0xb0,0x0b,0xba,0xbe,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x17,0x68,0x20,0x01,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x6a,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x18,0x28,0xa0,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x17,0x56,0xcc,
0x02,0x10,0x2a,0x00,0x1f,0x40,0x50,0x01,0x01,0x08,0x5d,0x17,0x77,0x03,0xb0,0xf5,0x41,0x33,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x23,0xc5,0xfe,0x80,0x73,0x01,0xd6,0xae,0x52,0xff,0xfe,0xd5,0x56,0xa5,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x23,0xc6,0x5c,0x91,0x58,0x08,0xc0,0x5a,0x4d,0xff,0xfe,0x65,0x9d,0x69,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x60,0x20,0x1b,0xfa,0xd4,0x00,0x02,0x0c,0x29,0xff,0xfe,0x61,0x4a,0x4c,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x60,0x20,0xb4,0x82,0x92,0x00,0x49,0x1a,0x35,0x8c,0xd8,0xf7,0x01,0xda,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0x13,0xdc,0xbc,0x00,0x55,0x59,0x02,0x58,0x02,0x7d,0xb5,0x2b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0x45,0x03,0x37,0x00,0x02,0x0c,0x29,0xff,0xfe,0x61,0x4a,0x4c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0xb4,0x34,0xeb,0x00,0xde,0xa6,0x32,0xff,0xfe,0x0d,0xa5,0xc0,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x60,0x20,0xb4,0x89,0x20,0x00,0x50,0x54,0x00,0xff,0xfe,0xfc,0x5e,0xd8,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x01,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0x7a,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x8a,0x60,0xe0,0x12,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0xae,0x40,0x24,0x0e,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xbb,0xe0,0x00,0xcc,0x00,0x00,0x5a,0x11,0x22,0xff,0xfe,0xb4,0x8f,0x5c,0x20,0x8d,
0x02,0x10,0x2a,0x00,0xbb,0xe0,0x00,0xcc,0x00,0x00,0x62,0xa4,0x4c,0xff,0xfe,0x23,0x75,0x10,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x0c,0xa8,0x0a,0x1f,0x30,0x25,0xf9,0x49,0xe4,0x42,0xc9,0x40,0x13,0xe8,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0xd4,0xe0,0x00,0x02,0xd0,0x02,0x44,0x67,0x31,0xe0,0x6f,0xa5,0xb3,0xef,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0c,0xa8,0x0a,0x15,0x9a,0x5b,0x8b,0x42,0xa8,0x86,0x7d,0x48,0x7a,0x21,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0c,0xa8,0x0a,0x1f,0xf9,0xb7,0xcb,0x55,0x57,0x66,0x52,0x4b,0xac,0xaa,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd4,0xe0,0x00,0xff,0xfc,0x02,0x5e,0x55,0x4a,0x7c,0xb8,0x3b,0xe5,0xa1,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd5,0x20,0x00,0x09,0x93,0x00,0x42,0x0b,0x54,0x4e,0x80,0x19,0x6d,0x3a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd8,0x80,0x00,0x05,0x00,0xc2,0x00,0x00,0x00,0x00,0x00,0x00,0xd3,0x29,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x0e,0xe2,0x12,0x00,0x19,0x00,0x08,0xd3,0xd2,0xff,0xfe,0xb1,0xbc,0x58,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x02,0x38,0x42,0x0f,0x92,0x00,0xfa,0x5a,0x1a,0x4b,0x1e,0x6a,0xfa,0xdf,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x02,0x38,0x43,0x89,0xc4,0x00,0x3b,0x26,0xd9,0x4e,0x38,0xd5,0x44,0xef,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0x90,0x00,0x16,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x4b,0x00,0x80,0x7c,0x31,0x00,0xcd,0xa1,0x0c,0x6a,0x2b,0xad,0x24,0x18,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x41,0x22,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x73,0x23,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x90,0x91,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x00,0x72,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x02,0x03,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x21,0x44,0xd7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x31,0x09,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x2a,0x1c,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x61,0x2b,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x61,0x3c,0xae,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x61,0x42,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x2b,0x02,0x9a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x4a,0x31,0xde,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x52,0x00,0x00,0x6c,0x61,0x62,0x7a,0x61,0x74,0x6b,0x6f,0x2e,0x73,0x6b,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x63,0x80,0xff,0xfe,0x00,0x73,0x10,0xfb,0xd0,0x12,0x85,0x81,0xb4,0xd7,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x3a,0x2d,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x07,0xa7,0x00,0x02,0x28,0x04,0xae,0x1f,0x6b,0xff,0xfe,0x9d,0x6c,0x94,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x07,0xc8,0xaa,0xac,0x00,0x89,0x50,0x54,0x00,0xff,0xfe,0xb7,0xf5,0xcb,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xaa,0xc2,0x01,0x80,0x50,0x54,0x00,0xff,0xfe,0x56,0x8d,0x10,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x07,0xc8,0xaa,0xc9,0x00,0xc9,0x50,0x54,0x00,0xff,0xfe,0xdf,0xff,0x95,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x01,0x01,0xc1,0x50,0x54,0x00,0xff,0xfe,0xee,0x3e,0x1a,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x09,0x02,0xaa,0x50,0x54,0x00,0xff,0xfe,0x1b,0xa1,0x96,0x2d,0x00,
- 0x02,0x10,0x2a,0x01,0x07,0xc8,0xff,0xfa,0x05,0x0e,0xdd,0xfe,0xc9,0x24,0xca,0x0a,0xcb,0xab,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x7e,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x59,0x66,0xdc,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x3b,0xbb,0x5b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x49,0x2f,0x5b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x87,0x40,0x00,0x01,0x07,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0xe5,0xcb,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x87,0x40,0x00,0x01,0xff,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x8c,0x6a,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x9f,0x40,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0xcb,0x00,0x0d,0x3d,0x77,0x00,0x02,0x27,0x0e,0xff,0xfe,0x28,0xc5,0x65,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x00,0x20,0x73,0x50,0x91,0x9c,0xb1,0xc3,0x8b,0x83,0xad,0xf9,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0xcb,0x00,0x0b,0x63,0xc0,0x00,0x02,0x27,0x0e,0xff,0xfe,0x28,0xc5,0x65,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0xcb,0x19,0x06,0x88,0xe9,0x00,0xaa,0x60,0xb6,0xff,0xfe,0x29,0xbb,0xae,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x01,0x63,0xc0,0xb0,0x9d,0xa5,0x16,0x90,0xa1,0x2b,0xbe,0xde,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x02,0x82,0x67,0xb0,0xb4,0xf4,0xaa,0xff,0xfe,0x7c,0x44,0xa6,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x0e,0x0a,0x03,0x01,0x70,0x10,0xb8,0x7d,0xe1,0x4b,0xce,0xa9,0xb9,0x98,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x04,0x8b,0x2d,0x10,0x94,0xf2,0x4d,0x5c,0xca,0x5f,0xbf,0x49,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x05,0x30,0xa0,0xa0,0xf4,0x65,0x0a,0xf5,0xbe,0x1b,0x90,0x75,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x0a,0xa7,0xc8,0xc0,0x96,0x79,0xaf,0xfa,0xb6,0xe5,0xef,0xc7,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x03,0x20,0x39,0xa0,0x32,0x5a,0x3a,0xff,0xfe,0x02,0x31,0x80,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x03,0x51,0x9f,0xb0,0x6b,0xf2,0x95,0xd6,0xb7,0xbd,0xb8,0x46,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x05,0xfa,0xa0,0xa0,0xca,0x1f,0x66,0xff,0xfe,0xce,0xb8,0xa2,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x08,0x3d,0xdd,0x30,0x36,0x76,0x5d,0x8e,0x8a,0x6f,0x11,0x5a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x09,0xe9,0xc2,0x40,0x7b,0x44,0xf3,0x2a,0x6e,0xc0,0xa8,0xaf,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x00,0xb5,0x7f,0x50,0xc2,0x57,0xa5,0x5b,0x48,0x46,0x97,0xe1,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x0e,0x11,0x10,0x0c,0x00,0x70,0xcb,0xc8,0x9e,0x31,0x4b,0x77,0x16,0x26,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x34,0xee,0x78,0x30,0x60,0x02,0x30,0x48,0xff,0xfe,0x81,0xf1,0xc6,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x12,0x10,0x14,0xa9,0x67,0x00,0x0a,0x00,0x27,0xff,0xfe,0x4e,0x82,0xb6,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x12,0x10,0x46,0x39,0x0f,0x00,0x10,0xa7,0xe9,0x65,0x50,0x9a,0x7a,0x4a,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x12,0x10,0x7c,0x92,0x51,0x00,0x02,0x11,0x32,0xff,0xfe,0xae,0x15,0x2d,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x12,0x10,0x86,0xbf,0xf1,0x00,0x31,0x78,0xd7,0x00,0xd4,0x4d,0x6b,0xb1,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x12,0x10,0x94,0x87,0xa2,0x00,0xed,0xc1,0x93,0xa4,0x09,0x45,0x9a,0x92,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x2c,0xdf,0x46,0x00,0x02,0xbc,0xe0,0x3e,0x43,0xe8,0x47,0x18,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x86,0xbf,0xf1,0x00,0xa9,0xac,0xd0,0x41,0x1f,0x8e,0x69,0x25,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x94,0xc3,0x34,0x00,0xd8,0xc3,0x74,0x3c,0x90,0xf6,0xa4,0x8a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x68,0x20,0x00,0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x01,0x68,0x42,0x0b,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x01,0x68,0x63,0x28,0x00,0x00,0x4a,0x21,0x0b,0xff,0xfe,0x26,0x38,0xc3,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x01,0x68,0x67,0x6e,0x00,0x00,0xe6,0x5f,0x01,0xff,0xfe,0x09,0x35,0x91,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x17,0x48,0xf3,0x9f,0x58,0x72,0xde,0xad,0xbe,0xef,0xb1,0xac,0xc0,0xfe,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x17,0x48,0xf3,0x9f,0x58,0x72,0x02,0x16,0x3e,0xff,0xfe,0x21,0x02,0x66,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x01,0x80,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x05,0x17,0x10,0xb6,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x21,0x68,0xa3,0x79,0xd1,0x00,0x96,0xde,0x80,0xff,0xfe,0xa3,0xfd,0x00,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x27,0x80,0x90,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x27,0x80,0x90,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x27,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x1a,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x2e,0x02,0x39,0x00,0x54,0x00,0xa0,0x99,0xe1,0xff,0xfe,0xb6,0x0d,0x0e,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x2f,0x05,0x66,0x0e,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x00,0x58,0x00,0x97,0x7d,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x6d,0x40,0x30,0x73,0x0c,0x01,0xde,0xa6,0x32,0xff,0xfe,0x44,0x4b,0x25,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x2f,0x05,0x60,0x08,0xce,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x03,0x90,0x90,0x00,0x00,0x00,0xaa,0xa1,0x59,0xff,0xfe,0x43,0xb5,0x7b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x05,0x78,0x85,0xce,0x16,0x00,0x1e,0x1b,0x0d,0xff,0xfe,0xe3,0x77,0x4b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x07,0x68,0xf9,0x2b,0xdb,0x46,0x5e,0x46,0x77,0x2b,0x07,0x1d,0x29,0xb7,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x7a,0x01,0x00,0x00,0x00,0x00,0x00,0x91,0x02,0x28,0x00,0x45,0x01,0x30,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0x50,0xd0,0xe3,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0x50,0xd1,0xe3,0x5b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x7b,0x40,0x59,0x28,0x00,0x89,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x7b,0x40,0xc3,0xb5,0xf5,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x83,0x08,0x80,0x87,0xaa,0x00,0x9e,0xa8,0x01,0xb2,0xef,0x98,0x56,0xbf,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0xb9,0x45,0x34,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0xd4,0x18,0x6d,0x9a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x80,0x70,0x0b,0x84,0x6a,0xe0,0xf9,0xc6,0xfb,0xb9,0x1c,0x41,0x81,0xaa,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x80,0x70,0xf1,0x86,0x38,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0xa6,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x80,0x84,0x01,0x03,0x68,0x10,0x1e,0x69,0x7a,0xff,0xfe,0xa2,0x1a,0xcc,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x83,0x08,0x80,0x81,0xf3,0x00,0x03,0xb8,0x7e,0xc0,0x28,0x37,0x1b,0x57,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x83,0x88,0xe3,0x02,0x79,0x80,0x6f,0x85,0xa0,0xb3,0x4b,0x4d,0x8b,0x0f,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x83,0x88,0xe5,0xc3,0x4a,0x80,0x02,0x01,0x2e,0xff,0xfe,0x82,0xb3,0xcc,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x84,0x2a,0x01,0xdf,0x8a,0x01,0x1e,0x1b,0x0d,0xff,0xfe,0x0b,0x23,0x6d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa2,0x10,0x28,0xbe,0x5f,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x11,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x4b,0x5c,0xf9,0x00,0x01,0xb6,0x2e,0x99,0xff,0xfe,0x49,0xd4,0x92,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xa4,0x4d,0x14,0xd6,0x00,0x01,0x02,0xc0,0x08,0xff,0xfe,0x8f,0xb3,0xb2,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xa4,0x5a,0x94,0xcd,0xf0,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xa4,0x5f,0x3b,0x9d,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xa4,0x67,0x78,0x33,0x00,0x01,0x72,0x85,0xc2,0xff,0xfe,0x2c,0x21,0xe9,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xaa,0x14,0x23,0x80,0xb3,0x00,0x40,0x40,0xbe,0x88,0x8b,0x01,0x0d,0x38,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x5f,0x3b,0x9d,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x99,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x64,0x3d,0x6b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x6c,0x7f,0x8e,0x00,0x01,0x35,0xbf,0x3a,0xeb,0x13,0x7c,0x1d,0x35,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x6d,0x03,0x6f,0x00,0x01,0x02,0x0d,0xb9,0xff,0xfe,0x4e,0x63,0x98,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x05,0x20,0x21,0x42,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xc2,0x06,0x20,0x44,0x98,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xc2,0x06,0x20,0x82,0x12,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xc2,0x06,0x30,0x08,0x23,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xc2,0x07,0x00,0x00,0x49,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x14,0xd4,
+ 0x02,0x10,0x2a,0x02,0xc2,0x06,0x20,0x75,0x33,0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x00,0x00,0x38,0x29,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x14,0x41,0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x24,0x61,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x14,0x87,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x26,0x66,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x34,0x73,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0xc2,0x07,0x30,0x02,0x74,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x30,0x08,0x45,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xcb,0x43,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x78,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x0e,0x5e,0x00,0x01,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x0e,0x98,0x00,0x20,0x15,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x06,0x41,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x06,0xf8,0x14,0x54,0x8b,0x17,0xff,0xfe,0x31,0xb6,0x4a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x28,0x00,0x68,0x74,0x11,0x53,0xff,0xfe,0x4c,0x02,0x1d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x65,0x0f,0xdc,0x34,0x62,0x66,0xff,0xfe,0x05,0xec,0x5c,0x20,0x8d,
0x02,0x10,0x2a,0x03,0x60,0x00,0x08,0x70,0x00,0x00,0x00,0x46,0x00,0x23,0x00,0x87,0x02,0x18,0x20,0x8d,
0x02,0x10,0x2a,0x03,0x94,0xe0,0xff,0xff,0x01,0x85,0x02,0x43,0x02,0x18,0x00,0x00,0x00,0x19,0x20,0x8d,
0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x01,0x00,0xe0,0x00,0x00,0x00,0x00,0x03,0x97,0x60,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x01,0x63,0x30,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x01,0x8a,0xd0,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x0f,0x3e,0x20,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0xe2,0xc0,0x13,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x0e,0xc0,0x00,0x00,0x09,0x28,0x00,0x00,0x00,0x00,0x07,0x01,0x07,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x03,0xc4,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8e,
- 0x02,0x10,0x2a,0x04,0x52,0xc0,0x30,0x07,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x01,0x00,0xe0,0x00,0x00,0x00,0x00,0x07,0x94,0x90,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x02,0x88,0xc0,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x0e,0x3b,0x50,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xcf,0xc0,0x80,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x5f,0xd6,0x35,0x57,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x21,0x80,0xdc,0x05,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x21,0x80,0xff,0xff,0xff,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x03,0xc4,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x04,0xbc,0x40,0x1d,0xc3,0x00,0x8d,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0x15,0x00,0x07,0x02,0x00,0x00,0x1c,0x00,0x40,0xff,0xfe,0x00,0x00,0x0c,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0x35,0x80,0xd1,0x01,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0x35,0x80,0xdb,0x0b,0x16,0x00,0xc4,0x89,0x76,0xed,0x31,0x3d,0x0b,0x33,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x01,0x81,0x27,0xaf,0xa7,0xda,0xf9,0xd9,0x1b,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x01,0xf6,0xab,0xdd,0x5e,0x40,0x39,0xb4,0x6c,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x03,0x65,0x23,0x50,0xa1,0x01,0x52,0xe8,0x8c,0x20,0x8d,
- 0x02,0x10,0x2a,0x05,0xd0,0x1a,0x0b,0x7b,0x3c,0x01,0x8b,0xf7,0xae,0x14,0xaf,0xb3,0x33,0xae,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0x35,0x80,0xdc,0x0b,0x16,0x00,0xde,0xf4,0x5a,0x62,0xde,0x42,0x32,0x4a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x00,0x8d,0xde,0x06,0x9f,0x4a,0xc7,0x0b,0x26,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x16,0x09,0x8f,0x52,0x01,0x6b,0xe0,0xa4,0xde,0x80,0xc7,0x32,0xd5,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x18,0x0a,0x75,0x6c,0x03,0x07,0x5b,0x2c,0x73,0x8c,0xaa,0x41,0x4b,0x20,0x8d,
0x02,0x10,0x2a,0x05,0xf4,0x80,0x18,0x00,0x06,0x97,0x54,0x00,0x02,0xff,0xfe,0xb6,0xc3,0x6d,0x20,0x8d,
0x02,0x10,0x2a,0x06,0xe0,0x40,0x76,0x03,0x29,0x18,0xc6,0xef,0x46,0x4e,0x9f,0xe5,0x73,0xec,0x20,0x8d,
- 0x02,0x10,0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x09,0x46,0x20,0x8d,
+ 0x02,0x10,0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x89,0x02,0x34,0x01,0x80,0x01,0x94,0x20,0x8d,
+ 0x02,0x10,0x2a,0x07,0xd8,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x7e,0x20,0x8d,
+ 0x02,0x10,0x2a,0x09,0x26,0x81,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x20,0x8d,
0x02,0x10,0x2a,0x09,0x26,0x81,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x20,0x8d,
- 0x02,0x10,0x2a,0x0a,0xc8,0x01,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x83,0x20,0x8d,
- 0x02,0x10,0x2a,0x0c,0x5a,0x80,0x12,0x10,0xa8,0x00,0x6a,0xf7,0x28,0xff,0xfe,0xe5,0x6b,0x3a,0x20,0x8d,
- 0x02,0x10,0x2a,0x0d,0x56,0x00,0x00,0x24,0x0a,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,0xa9,0x1e,0xd8,0x4d,
- 0x02,0x10,0x2a,0x0d,0x7c,0x40,0x30,0x00,0x0b,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0b,0xf3,0x00,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x0d,0x83,0x40,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x0f,0xdf,0x00,0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x62,0x20,0x8d,
- 0x02,0x10,0x2a,0x10,0x37,0x81,0x16,0xb9,0x00,0x01,0xfe,0x3f,0xdb,0xff,0xfe,0x04,0x2d,0x4c,0x20,0x8d,
- 0x02,0x10,0x2a,0x10,0x37,0x81,0x08,0x4b,0x00,0x01,0xb1,0x23,0x63,0x06,0x94,0x3a,0xf0,0x9b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0e,0x8f,0x02,0x21,0xd1,0x01,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0e,0xb7,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xd1,0xf0,0x5b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x10,0x37,0x81,0x2c,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x10,0xd2,0x00,0x00,0x01,0x00,0x33,0xa6,0xbf,0x01,0xff,0xfe,0x6a,0x46,0xa9,0x20,0x8d,
- 0x02,0x10,0x2c,0x0f,0xf4,0xc0,0x22,0x02,0x20,0xb0,0x26,0x1c,0x04,0xff,0xfe,0x14,0xda,0xa0,0x20,0x8d,
- 0x02,0x10,0x2c,0x0f,0xf8,0xf0,0xda,0x51,0x00,0x00,0x70,0xc3,0xee,0xa9,0x97,0x17,0x95,0x79,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x12,0x8e,0x40,0x56,0x68,0xe4,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2c,0x0f,0xf8,0xf0,0xda,0x51,0x00,0x00,0x3a,0x45,0xfc,0x57,0x5e,0x30,0x25,0x93,0x20,0x8d,
+ 0x04,0x20,0xd0,0x60,0x63,0xb5,0x63,0xa7,0x98,0x2a,0x1b,0x8f,0x42,0xb2,0xf8,0xd8,0xf3,0x4a,0x5b,0x2c,0x10,0x64,0x88,0xa7,0x51,0x23,0xca,0x58,0xad,0x92,0x3d,0xa9,0x1f,0x3c,0x20,0x8d,
+ 0x04,0x20,0xe2,0xe3,0xbb,0x69,0x18,0xc2,0xfb,0x4e,0xaf,0xd3,0x74,0x91,0x91,0x2f,0x0a,0x2f,0x6b,0xb9,0x32,0x11,0x74,0x06,0xdd,0x23,0x87,0xda,0xa1,0xe7,0x94,0x21,0x48,0x75,0x20,0x8d,
0x04,0x20,0xe9,0xbf,0xa7,0xbd,0x9b,0x54,0x54,0xe8,0xc8,0xae,0x78,0x99,0xa0,0xa3,0xf6,0x5d,0x78,0xe3,0x9e,0x5c,0xa7,0x18,0xb9,0x13,0x0c,0x04,0x9b,0xf3,0x7f,0x27,0x18,0xb0,0x20,0x8d,
+ 0x04,0x20,0xec,0x82,0xc1,0x2f,0x8c,0xe8,0x1e,0x8e,0xce,0x00,0x2a,0x38,0x61,0x09,0x51,0x6e,0xf1,0xdd,0x4b,0x3d,0x64,0x95,0x72,0xee,0x7e,0x45,0xb8,0x17,0xfc,0x91,0xb2,0x1f,0x20,0x8d,
+ 0x04,0x20,0xff,0xfc,0x02,0x19,0x7f,0x99,0x4e,0x6f,0x39,0x10,0x86,0x3f,0xe4,0xb6,0xd1,0xb0,0x6a,0x2c,0x4e,0x57,0xd4,0x08,0x61,0x18,0xfa,0x62,0x61,0x44,0xad,0x7c,0xdf,0xfc,0x20,0x8d,
+ 0x04,0x20,0xff,0xd8,0x39,0x93,0x9a,0x94,0x57,0xcd,0x48,0xe3,0x3e,0x98,0x79,0xa6,0x3f,0x69,0x7e,0x36,0x10,0x70,0x8d,0xbc,0x15,0x08,0x64,0x51,0xc7,0x56,0x35,0x63,0xf3,0x93,0x20,0x8d,
+ 0x04,0x20,0x06,0x42,0xf9,0x1f,0x80,0x8c,0x16,0xcf,0x73,0x9a,0xb1,0x50,0xcc,0x7e,0x11,0xf7,0x42,0xc3,0x83,0x59,0x39,0x92,0x6f,0xd8,0x20,0x4a,0x3d,0x5a,0x82,0x86,0xee,0xcf,0x20,0x8d,
0x04,0x20,0x0f,0xb9,0x71,0x05,0x64,0x83,0x2c,0x68,0x6a,0x9c,0xf0,0x4f,0xc3,0x90,0xcd,0x5c,0x73,0x9a,0xdd,0xb3,0xc6,0x42,0xca,0x09,0xbb,0xcc,0xfe,0x29,0x49,0x9f,0xc7,0x28,0x20,0x8d,
- 0x04,0x20,0x2a,0x47,0x8b,0xa0,0x4f,0x67,0x1d,0xcd,0x5d,0x84,0x1a,0xec,0xbd,0xd2,0xaa,0xe9,0x99,0x01,0x96,0x5d,0x4e,0xff,0x64,0x47,0xba,0xde,0xbf,0x56,0x89,0x39,0xac,0xde,0x20,0x8d,
+ 0x04,0x20,0x0c,0xa0,0x19,0x89,0xe0,0x8c,0x4a,0x83,0xae,0xce,0x5c,0xf9,0x73,0x24,0x25,0xe0,0x8d,0x01,0x2a,0xb1,0xac,0x8b,0x8a,0xe3,0xd5,0x3a,0x21,0x16,0x9f,0x96,0xf1,0x8e,0x20,0x8d,
+ 0x04,0x20,0x0c,0xa0,0x19,0x8a,0x74,0x59,0x74,0x6e,0x26,0x69,0x90,0xca,0x65,0xe8,0x83,0xfc,0x25,0xec,0xe7,0xb6,0x85,0xf5,0xf8,0x37,0x08,0x42,0xbe,0xb0,0x38,0x4a,0x8e,0x2b,0x20,0x8d,
+ 0x04,0x20,0x15,0x91,0xb2,0x2a,0x36,0x3a,0x0d,0xcf,0x8c,0xe7,0xc9,0x48,0xd8,0xf4,0xe1,0xa8,0x94,0x9f,0x97,0x20,0x72,0x70,0x6a,0x83,0x28,0xd2,0xe8,0x29,0x11,0xb6,0x0e,0x6c,0x20,0x8d,
+ 0x04,0x20,0x19,0x2a,0x86,0x86,0x62,0x65,0x95,0x44,0x4e,0xb7,0x5e,0xe6,0x54,0x75,0xc5,0xe6,0x41,0x4e,0x7b,0x0c,0x20,0x1a,0xd5,0x26,0xbe,0x2b,0xdb,0x85,0xa8,0xc5,0xdc,0x3a,0x20,0x8d,
+ 0x04,0x20,0x19,0x2a,0x86,0x86,0x62,0x9d,0x3f,0x41,0x4d,0x7f,0x3a,0xf1,0xa4,0xd2,0x9b,0x99,0xc2,0x62,0x22,0xa5,0x6d,0x6d,0xb1,0x29,0x3b,0x79,0xf1,0xe6,0xe2,0xaf,0x6d,0xfd,0x20,0x8d,
+ 0x04,0x20,0x19,0x2a,0x86,0x86,0x62,0xc2,0xbd,0xcd,0xa2,0x34,0xc9,0x5e,0x0b,0xdb,0x6e,0x6e,0x92,0xa2,0x4f,0xf1,0x0a,0x59,0x6c,0x19,0x9c,0xd0,0xd8,0x3c,0x50,0x3b,0x41,0x27,0x20,0x8d,
+ 0x04,0x20,0x1c,0xe0,0xbe,0xd4,0x48,0xe2,0x00,0xc5,0xf0,0x0d,0x65,0x08,0x59,0xc2,0x3f,0x09,0x4d,0x98,0x2c,0x14,0xc9,0x71,0xc8,0x8b,0x53,0x29,0xa8,0xb5,0x8c,0x7b,0x22,0xb0,0x20,0x8d,
+ 0x04,0x20,0x23,0x33,0x80,0xcc,0x5a,0xeb,0x86,0x37,0x03,0x4f,0x8b,0x97,0xac,0xb5,0xf9,0xd0,0x2e,0xd8,0x9b,0xca,0xff,0xef,0x63,0x22,0x2c,0x3d,0x7e,0x83,0x39,0x14,0x10,0xc7,0x20,0x8d,
+ 0x04,0x20,0x23,0x33,0x80,0xcc,0x5b,0x25,0x8f,0xfc,0xa0,0xef,0x50,0xeb,0x4b,0x2c,0xd7,0x89,0xf2,0x2b,0xe3,0x4f,0xe9,0x6a,0x93,0x19,0xff,0x69,0x92,0x1b,0x8b,0x13,0xbd,0x67,0x20,0x8d,
+ 0x04,0x20,0x23,0x33,0x80,0xcc,0x5c,0xef,0x09,0x60,0xd5,0x10,0x6b,0x0d,0x33,0x5f,0x71,0x35,0x02,0xb9,0x19,0x4a,0xd6,0xe3,0xc7,0x34,0x27,0x40,0xc5,0x5d,0xfa,0xd1,0x23,0x28,0x20,0x8d,
+ 0x04,0x20,0x23,0x33,0x80,0xcc,0x5d,0x0b,0x5e,0x1d,0x3b,0xc6,0x3f,0x78,0xe8,0x51,0x0e,0xbf,0x78,0xee,0x47,0xf8,0x6f,0x01,0xab,0x21,0x16,0xcc,0x52,0xe6,0xad,0xd4,0x02,0x02,0x20,0x8d,
0x04,0x20,0x2b,0xf3,0xe8,0xf5,0xef,0x90,0x14,0xab,0x61,0xe9,0x11,0x97,0x9f,0x18,0x4d,0xb4,0xff,0x89,0x94,0xf7,0x92,0x94,0x53,0xe6,0x9e,0xd4,0xdb,0x85,0x89,0x4d,0x3e,0xc9,0x20,0x8d,
- 0x04,0x20,0x35,0xdd,0xd0,0x36,0xa5,0x69,0x4a,0xd2,0xcc,0xb8,0xe9,0x62,0xa3,0x55,0xeb,0x86,0xe2,0xf3,0x03,0x48,0x26,0xe6,0x20,0xad,0xda,0xaa,0xff,0xde,0x16,0xad,0x39,0x9d,0x20,0x8d,
- 0x04,0x20,0x41,0x47,0x4e,0xc2,0xa1,0x71,0x63,0x3e,0x11,0x54,0x46,0x91,0x80,0xed,0x41,0x16,0x32,0x29,0x19,0x60,0xc9,0xef,0xa3,0xb7,0x96,0x2c,0x94,0xa8,0xdf,0x55,0xd7,0x21,0x20,0x8d,
- 0x04,0x20,0x44,0xf3,0xb7,0x5e,0x48,0x3c,0xbd,0xa6,0x52,0xaa,0x68,0xb5,0xbf,0xdc,0x01,0x5f,0x4b,0xeb,0x7a,0x25,0xcb,0x4a,0x70,0xbc,0x18,0x8c,0x97,0x5d,0x27,0x54,0x09,0x17,0x20,0x8d,
+ 0x04,0x20,0x38,0x1b,0x57,0x6c,0xf9,0x80,0x4e,0x28,0x33,0xb5,0x01,0xce,0x8e,0x83,0x62,0xcd,0xee,0x3b,0x0b,0xf5,0x9f,0x7c,0xfe,0x9a,0x3f,0x0a,0x0a,0x2a,0x91,0x60,0x85,0x23,0x20,0x8d,
+ 0x04,0x20,0x3e,0x7e,0xe8,0x36,0x75,0x4f,0x00,0x38,0xda,0x70,0x03,0xde,0x23,0x02,0xe5,0x8a,0x02,0x6e,0xd3,0x91,0xa9,0x54,0xfb,0x2d,0xdb,0xbd,0x1f,0xd2,0x8a,0x98,0xae,0x16,0x20,0x8d,
+ 0x04,0x20,0x44,0xf1,0x1b,0x9d,0x03,0xaa,0x5a,0x96,0x97,0xc8,0x66,0x71,0x48,0x41,0xaa,0x55,0xa2,0x81,0xa5,0xa4,0x0a,0x82,0xe5,0x5f,0x5d,0x93,0xb7,0x2a,0x43,0x65,0xc9,0x32,0x20,0x8d,
+ 0x04,0x20,0x4c,0xdd,0xa4,0x94,0x32,0xb9,0xc3,0xec,0x54,0xe2,0x8c,0x52,0x31,0xc4,0xc1,0x02,0xa6,0x4f,0x4c,0xd2,0xc6,0xc5,0xbc,0xed,0xf1,0x94,0xbd,0xc3,0x34,0x66,0xab,0xab,0x20,0x8d,
+ 0x04,0x20,0x57,0xda,0x1d,0xc7,0xe7,0xba,0x3a,0x4b,0xd6,0x3c,0xbb,0x2a,0xd4,0x2c,0xe5,0xb7,0x8e,0x44,0x7e,0x56,0xe4,0x6d,0x9d,0x4f,0xce,0x69,0xf8,0xfc,0x7a,0xc1,0x74,0x41,0x20,0x8d,
0x04,0x20,0x53,0xcd,0x56,0x48,0x48,0x8c,0x47,0x07,0x91,0x41,0x82,0x65,0x5b,0x76,0x64,0x03,0x4e,0x09,0xe6,0x6f,0x7e,0x8c,0xbf,0x10,0x84,0xe6,0x54,0xeb,0x56,0xc5,0xbd,0x88,0x20,0x8d,
+ 0x04,0x20,0x5f,0xd5,0x6d,0xd5,0x32,0xe0,0x8b,0xd6,0x36,0x84,0x4a,0x4d,0x01,0x5e,0x36,0xf5,0xad,0x24,0xb0,0x01,0x5a,0xe1,0xb3,0xec,0x0c,0xc7,0x0b,0xd8,0x6f,0x63,0xf0,0x8c,0x20,0x8d,
0x04,0x20,0x67,0xc4,0x17,0xa5,0xcb,0x77,0xbd,0xaa,0x11,0x7f,0x8b,0xc0,0x81,0xf3,0xc0,0x96,0x9d,0x31,0x27,0x9c,0xad,0x6c,0x6d,0x98,0x42,0x70,0xdb,0x50,0x12,0x96,0x0b,0x36,0x20,0x8d,
+ 0x04,0x20,0x63,0xac,0x1d,0x42,0xcf,0x49,0xa5,0xa0,0x7a,0x1b,0xc2,0x77,0x70,0x7d,0xb7,0x52,0xcb,0x29,0x51,0x7c,0xb7,0xf0,0xd7,0x37,0x18,0x15,0xb7,0x4c,0x39,0xe5,0xa6,0xef,0x20,0x8d,
0x04,0x20,0x65,0x98,0x55,0xd0,0x8a,0xe0,0x29,0xe6,0x5e,0xef,0xb7,0x8b,0x8f,0xc9,0x27,0x53,0x3d,0xd0,0x8c,0xa2,0xfa,0x32,0x2f,0xad,0xf9,0xdc,0xe2,0x4b,0x14,0x66,0x3e,0x23,0x20,0x8d,
- 0x04,0x20,0x8b,0xfe,0xad,0x19,0xdb,0x97,0x57,0x84,0xec,0xad,0x4f,0xb2,0xdf,0x69,0x53,0x04,0x57,0x19,0x16,0x7a,0x71,0xd7,0x2b,0xab,0x03,0xfd,0x76,0x4d,0xa0,0x70,0xc3,0xe7,0x20,0x8d,
+ 0x04,0x20,0x89,0x60,0xc6,0xee,0x04,0xdf,0xa8,0x07,0xcd,0x57,0x38,0xea,0x8f,0xf1,0xfc,0x3e,0x23,0x93,0xa2,0x3e,0xa9,0xd9,0x90,0xe8,0xf0,0x8e,0x2e,0xe1,0xa0,0xdc,0x35,0x09,0x20,0x8d,
+ 0x04,0x20,0x8c,0x8d,0x69,0xb5,0xd4,0x91,0x45,0xfc,0xe0,0xdc,0x84,0x99,0xb3,0x54,0x59,0x4d,0xfe,0x24,0x1c,0xfa,0x6a,0x70,0x90,0xa5,0x7b,0x85,0x44,0x9d,0xdc,0xdf,0xb3,0x2a,0x20,0x8d,
+ 0x04,0x20,0x96,0x87,0xdd,0x04,0x33,0x9d,0x2c,0x25,0xed,0xe3,0x8d,0xb2,0x7b,0xbd,0x87,0x9d,0xdb,0xb4,0x9b,0x5b,0x38,0x2a,0x98,0x1b,0xa4,0xc1,0xac,0x79,0xa2,0xde,0x59,0xb0,0x20,0x8d,
+ 0x04,0x20,0xa3,0xeb,0x33,0xe3,0xfc,0x39,0xaa,0x60,0xc1,0x1e,0xb6,0xf2,0x3d,0x31,0x62,0x3b,0x91,0xd9,0xd6,0xb6,0xf8,0x7a,0x77,0xb1,0x87,0x10,0xc6,0x3e,0x95,0xad,0xb4,0x51,0x20,0x8d,
+ 0x04,0x20,0xb1,0xdd,0xb8,0x4b,0x84,0x82,0xe1,0x97,0x5c,0xf0,0x81,0x80,0xde,0x30,0xdd,0xe5,0x01,0xda,0x67,0x6c,0x57,0x16,0x9d,0xec,0x06,0xee,0xc8,0x8e,0x3a,0x35,0x6e,0x5b,0x20,0x8d,
+ 0x04,0x20,0xb2,0x32,0x14,0x4f,0xbd,0xef,0x43,0x29,0x2e,0xd5,0xe0,0xa7,0x17,0x35,0xb2,0x32,0xbb,0x53,0xea,0xda,0x78,0xef,0xfa,0xfc,0x98,0xa2,0x2a,0xbc,0x51,0xd3,0x21,0x12,0x20,0x8d,
+ 0x04,0x20,0xc4,0x58,0x41,0xc7,0x7d,0x9c,0xf9,0x68,0x15,0xb6,0x32,0x16,0xb9,0xdc,0xfd,0x0b,0x68,0xa2,0x51,0x57,0x4b,0xcc,0x6d,0xa7,0x2c,0x64,0xb0,0xb1,0xf5,0x66,0x93,0x7a,0x20,0x8d,
0x05,0x20,0xd7,0x7a,0x53,0x89,0xfe,0x02,0x6a,0x59,0xb7,0x0e,0xf8,0x6d,0x9d,0x81,0xbb,0x9f,0x01,0xc0,0xc4,0xee,0x7b,0x36,0x10,0x33,0x07,0xd2,0x29,0xd8,0xec,0xcd,0x8e,0xa3,0x00,0x00,
0x05,0x20,0xd7,0xf1,0x19,0x9e,0x7d,0x0f,0x43,0x97,0x33,0x56,0xe8,0x12,0x1d,0x7d,0xa0,0x4d,0x21,0x5a,0x60,0x73,0xc8,0x7e,0x10,0x55,0x60,0x56,0xbb,0x65,0x50,0xa4,0x17,0x59,0x00,0x00,
- 0x05,0x20,0xd1,0x17,0xe2,0x34,0x4a,0x61,0x70,0x8b,0x04,0xa2,0xb4,0xb0,0x65,0x59,0x52,0x75,0x67,0x86,0xe6,0x48,0x33,0x31,0x5d,0x87,0x38,0x57,0xd3,0xf8,0x40,0x07,0x73,0xa8,0x00,0x00,
0x05,0x20,0xd9,0x9c,0x20,0xfe,0xc2,0xe6,0x6a,0x16,0x30,0x81,0x54,0xc9,0x3f,0x9a,0x89,0x10,0xa9,0x4b,0xf1,0x05,0x56,0xd5,0x04,0x2d,0xb7,0x6a,0x7b,0x67,0x8d,0xf0,0xbe,0x8f,0x00,0x00,
- 0x05,0x20,0xdc,0xdb,0x2d,0x39,0xd5,0xe4,0xda,0xb5,0xb6,0x6e,0x98,0x33,0x8f,0x51,0x99,0x54,0x38,0x53,0xa8,0x4e,0xda,0x29,0xd8,0xb5,0x57,0x9d,0x36,0xf0,0x97,0x37,0x18,0x3f,0x00,0x00,
0x05,0x20,0xe1,0x44,0x2d,0x6e,0xd3,0xd9,0xf0,0x95,0x6c,0x52,0x2e,0x44,0x3c,0x27,0x3d,0x78,0xac,0x6e,0x8f,0x27,0x1c,0x0c,0xc0,0x78,0x22,0x3e,0xa1,0x84,0x01,0x42,0x08,0x5c,0x00,0x00,
0x05,0x20,0xe3,0xa5,0x88,0x11,0x4d,0x3d,0xfb,0x02,0xec,0x1f,0xda,0x48,0x86,0x12,0xf6,0x12,0xd9,0x3e,0x68,0x49,0xa7,0xae,0x37,0xfd,0x02,0x48,0x38,0x8b,0xdc,0xd4,0xa6,0x8f,0x00,0x00,
0x05,0x20,0xe5,0x19,0x24,0x71,0xab,0x61,0xb0,0xfe,0x44,0x4a,0x74,0x8d,0xca,0x90,0xc3,0xd6,0x24,0xb4,0xd5,0x03,0xe7,0xf3,0x4f,0xbe,0x12,0x72,0xd6,0xa0,0x4b,0x22,0x0b,0xe1,0x00,0x00,
- 0x05,0x20,0xee,0xab,0xea,0x3b,0xc2,0x8a,0xe2,0xb3,0xe5,0xe7,0x92,0xf0,0x88,0x05,0x68,0x8d,0x2f,0xe7,0x2c,0xd2,0x00,0x39,0xd4,0x5d,0x07,0xfc,0xa1,0xd6,0xbf,0x89,0xa2,0x40,0x00,0x00,
0x05,0x20,0xf2,0x74,0x4c,0x90,0xc3,0xd9,0x34,0x4d,0x5f,0x6e,0xdb,0xdd,0x7d,0xef,0xa5,0xed,0x6e,0x59,0x9e,0x31,0x41,0x94,0x38,0x84,0xc5,0x08,0xd2,0x23,0xb3,0xa7,0xe0,0x2c,0x00,0x00,
0x05,0x20,0xf3,0x77,0xe5,0xa7,0x11,0xef,0x65,0x91,0x23,0xb8,0x32,0x06,0xcb,0xc0,0x91,0xf7,0x21,0x1d,0x70,0xbc,0x83,0x1b,0x86,0x34,0x35,0x31,0x0f,0x9f,0xc1,0x0d,0xbb,0x56,0x00,0x00,
0x05,0x20,0xfe,0xb0,0x99,0x79,0x95,0x58,0x71,0xb5,0x63,0xcc,0x33,0xeb,0x55,0x91,0x8c,0xb4,0x3a,0xf2,0x8b,0x2d,0x8e,0x47,0xbe,0x25,0x47,0x12,0xcd,0x14,0x48,0xf0,0x1d,0xea,0x00,0x00,
- 0x05,0x20,0xfc,0x79,0x14,0x77,0x6b,0x0e,0x34,0x8d,0xde,0x01,0x33,0xed,0xb4,0x0e,0xa7,0xc9,0x15,0x4f,0xd0,0x27,0x2c,0xd6,0x5f,0xe9,0x63,0x82,0x8d,0xd5,0x0c,0x9e,0x18,0x29,0x00,0x00,
0x05,0x20,0x07,0x61,0x26,0xd7,0x6c,0x05,0xbf,0xf6,0x2d,0x8c,0xca,0xc4,0x65,0xd3,0xd3,0xb2,0x49,0xe9,0xcc,0x53,0x1e,0xca,0x77,0x84,0xb6,0x10,0x5e,0xc2,0x5a,0xfe,0x28,0xb3,0x00,0x00,
0x05,0x20,0x03,0xaa,0x47,0xe9,0xe2,0x77,0xeb,0xa5,0x72,0x27,0x23,0x8b,0x13,0x62,0x61,0x32,0xb5,0xb2,0x1b,0x5a,0x18,0xb2,0xf9,0x26,0x06,0x84,0xee,0x28,0x42,0xac,0xba,0xbc,0x00,0x00,
- 0x05,0x20,0x08,0xc6,0x19,0x31,0x40,0x96,0xf3,0xe2,0x81,0x4e,0x88,0x54,0x54,0x9e,0xbf,0xfa,0x38,0x7a,0xfa,0x38,0x96,0x13,0x2a,0xc4,0x69,0xa2,0xae,0xe5,0x94,0xc7,0x16,0xb7,0x00,0x00,
0x05,0x20,0x0a,0x26,0x27,0x23,0xdd,0xf3,0x56,0xbe,0x9e,0x9e,0xa7,0xc6,0x3c,0xc5,0x99,0xc4,0x87,0x3b,0x4d,0xb9,0x13,0x62,0x91,0xf2,0x25,0x1c,0x02,0x42,0x63,0xe3,0x63,0x7a,0x00,0x00,
0x05,0x20,0x0c,0x50,0x55,0x46,0x87,0x5a,0x8d,0x14,0xfb,0xa7,0x29,0x70,0x18,0xa6,0x29,0x80,0x8c,0x33,0x42,0x5a,0x8f,0xe4,0x84,0x64,0x3d,0x0e,0xb5,0xbd,0x36,0x34,0x42,0xb6,0x00,0x00,
0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x00,0x00,
0x05,0x20,0x18,0x31,0xb3,0x9a,0xf8,0x8c,0xec,0x99,0x2e,0x7d,0xe4,0x90,0xa2,0x54,0x27,0xbd,0xe5,0xc8,0x65,0xdf,0x1f,0xaa,0x8f,0xe9,0x0f,0x64,0x85,0x09,0xc3,0x70,0x62,0x13,0x00,0x00,
- 0x05,0x20,0x1a,0x35,0x98,0x78,0xb1,0xd9,0x48,0x62,0xe9,0x23,0x10,0xfe,0x71,0xdb,0x10,0x5f,0x28,0xc8,0xf3,0xda,0x33,0x9b,0x26,0xcb,0x4d,0xbe,0x7b,0x03,0x76,0xb9,0xe0,0x54,0x00,0x00,
+ 0x05,0x20,0x1d,0x3a,0xac,0x0e,0x8c,0x62,0x35,0xb0,0xa4,0xaf,0xf0,0x47,0xf7,0x5a,0x55,0x8a,0x12,0x69,0xff,0x27,0xad,0xd9,0x9e,0x9f,0xa5,0xec,0x9e,0x24,0x38,0x8e,0x24,0xed,0x00,0x00,
0x05,0x20,0x27,0x7a,0xaf,0x5a,0x9c,0xf4,0x72,0xfe,0x3c,0xdd,0x7a,0xba,0xd7,0x98,0x31,0xde,0x73,0xce,0x84,0x5b,0x41,0xe7,0x9a,0x6a,0xe2,0xc1,0x3b,0x5b,0x37,0x23,0xc7,0xdf,0x00,0x00,
0x05,0x20,0x20,0x90,0xe3,0xd3,0xad,0x87,0xeb,0x2a,0xd9,0x29,0x17,0x74,0x47,0xc9,0x54,0x57,0xfa,0x3d,0x71,0x02,0x11,0xb2,0xc3,0x87,0x31,0xb3,0x9b,0x6f,0x2e,0xfc,0x30,0xea,0x00,0x00,
0x05,0x20,0x22,0x56,0xd6,0x98,0x11,0x61,0xe1,0x5a,0x34,0x9f,0xe2,0x9d,0xf5,0x2b,0xbd,0xbc,0xcc,0x1c,0xf5,0x1d,0x68,0xa5,0xca,0xb1,0xb5,0x4b,0xf1,0xb5,0xff,0x1e,0xdd,0xc0,0x00,0x00,
- 0x05,0x20,0x37,0x3e,0x28,0x39,0xef,0xa6,0xbc,0xf8,0xf1,0xba,0x11,0x40,0x87,0x42,0xff,0x58,0x46,0x8f,0xb0,0x80,0xfe,0x20,0x75,0xc5,0x43,0xce,0xec,0x9b,0x5d,0x37,0x19,0xf4,0x00,0x00,
- 0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x00,0x00,
+ 0x05,0x20,0x29,0xf2,0xb7,0xee,0xf1,0x70,0x02,0xe3,0xb5,0x89,0x73,0x69,0xd4,0x40,0x19,0xb1,0xd6,0x10,0xd8,0xfd,0x13,0xe8,0x9a,0xdb,0x40,0xc8,0xa9,0xb9,0x46,0x03,0x11,0x12,0x00,0x00,
+ 0x05,0x20,0x2d,0xfc,0xd7,0xed,0xc7,0x20,0x05,0x1d,0xcf,0xe6,0x5d,0x0b,0x38,0xc1,0xda,0x85,0xd6,0x30,0x84,0x13,0x35,0xb8,0x72,0x3d,0xff,0xa2,0xc3,0xc4,0xf6,0x38,0xef,0xc4,0x00,0x00,
+ 0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x00,0x00,
0x05,0x20,0x39,0xca,0x8e,0x62,0x0a,0x36,0xa7,0x68,0x22,0xc4,0xcc,0x4a,0xa9,0x16,0x69,0x4b,0x8a,0x1c,0x5f,0x6e,0x4a,0x98,0xb6,0x95,0x82,0xb3,0x66,0x66,0xc5,0x29,0x3a,0xb0,0x00,0x00,
- 0x05,0x20,0x3b,0xd0,0x80,0xc4,0xab,0x82,0x83,0x11,0x48,0xe5,0x3b,0x23,0x3b,0x17,0x04,0x0f,0xd8,0x39,0x7c,0x1b,0x70,0x73,0x68,0x98,0x11,0x22,0x49,0x51,0x8f,0x20,0x65,0x65,0x00,0x00,
- 0x05,0x20,0x47,0x1f,0x83,0xc8,0xa3,0x87,0x35,0xcf,0x8e,0x8d,0x22,0xfe,0xf0,0x02,0x63,0x04,0x3a,0x6a,0x49,0x1a,0x3b,0x70,0x17,0xeb,0x05,0xed,0xaf,0x61,0xb6,0x26,0xb8,0x21,0x00,0x00,
- 0x05,0x20,0x45,0xbd,0x33,0x3d,0x81,0x1e,0x14,0x52,0x88,0x8a,0xa7,0x46,0x0f,0x37,0x25,0xd0,0x59,0x9f,0x78,0xb5,0x7f,0x4a,0xf1,0x54,0x8c,0x2d,0x36,0x32,0xf8,0x10,0xf3,0xcc,0x00,0x00,
- 0x05,0x20,0x4a,0x8b,0x40,0x25,0xdc,0x06,0x2a,0xed,0x44,0x35,0xec,0x06,0x9e,0x73,0x70,0xf0,0x07,0x06,0x35,0xd1,0x60,0x4f,0x22,0xe8,0xbf,0x8a,0xdf,0xd9,0xeb,0x97,0x73,0x06,0x00,0x00,
0x05,0x20,0x4e,0x77,0x2e,0x12,0x91,0x67,0x6b,0x94,0xc4,0x92,0x2f,0x19,0x67,0x7d,0xcd,0x47,0x02,0xad,0xf8,0x60,0x72,0xed,0x73,0xf1,0x10,0x99,0x2c,0x05,0x61,0x66,0x55,0xd9,0x00,0x00,
0x05,0x20,0x5a,0x29,0xfe,0x8a,0xaa,0x9d,0x78,0x81,0x04,0x53,0x37,0xf5,0x6f,0xb6,0xe1,0x57,0x08,0x80,0xcf,0xf6,0x03,0x11,0x92,0x8d,0x08,0xe3,0x99,0x9f,0x98,0x4a,0x27,0x6b,0x00,0x00,
- 0x05,0x20,0x5a,0x65,0x0a,0x52,0x9b,0xc7,0x2e,0x92,0x79,0x7b,0xd3,0xf7,0xa4,0x35,0xbe,0x8e,0xb1,0xea,0x2d,0x7e,0x73,0x84,0x6a,0x3f,0xc9,0x9a,0x62,0xe3,0xc6,0x17,0x0e,0x7c,0x00,0x00,
0x05,0x20,0x5c,0x40,0x7f,0x80,0x43,0x91,0x9c,0xfc,0x04,0xdc,0xdc,0x8e,0x01,0xda,0xc8,0xaf,0x90,0x62,0x64,0x16,0xf7,0x11,0xe4,0x87,0xac,0xa4,0x06,0x6f,0x8d,0x87,0x4e,0xd6,0x00,0x00,
0x05,0x20,0x5e,0x69,0x4f,0x31,0x33,0xa7,0xea,0x3e,0xf4,0x7a,0x0a,0x1e,0x74,0x09,0x07,0xa1,0x50,0xf7,0x03,0xf5,0xc6,0x19,0xeb,0x95,0xaa,0x63,0x17,0x10,0x6e,0x68,0xca,0x10,0x00,0x00,
0x05,0x20,0x67,0x82,0xfc,0x36,0xea,0xae,0x95,0x3b,0x5d,0x46,0xf3,0xf4,0x6c,0x50,0x69,0x29,0xc7,0x47,0x87,0xca,0xa6,0x40,0x12,0x40,0x6d,0x12,0x94,0x35,0x17,0x8a,0xba,0x56,0x00,0x00,
0x05,0x20,0x67,0xab,0xce,0xf2,0xe3,0xf3,0xf6,0x19,0xf6,0x56,0xa2,0x4c,0xca,0x91,0x4b,0x93,0xfe,0xbc,0x85,0x50,0x5c,0x20,0x51,0x69,0xfb,0xf0,0x92,0xbb,0x57,0xa3,0x0d,0x05,0x00,0x00,
0x05,0x20,0x62,0xcc,0x44,0x66,0x31,0x76,0x1b,0x43,0xbe,0xe1,0xc9,0x1d,0x20,0x26,0x7d,0xa7,0x06,0x31,0x57,0x0d,0xb9,0x58,0x20,0xb3,0xca,0xb2,0x5e,0x0a,0x15,0x0b,0xba,0x0d,0x00,0x00,
+ 0x05,0x20,0x77,0xa7,0xc2,0xc7,0xa0,0xc1,0x40,0x3c,0xa0,0x94,0x9c,0xa6,0x6b,0x09,0x6b,0xad,0xae,0x4a,0x65,0x2b,0xbb,0x33,0x26,0x3d,0x3b,0x3b,0xd5,0x52,0x14,0xf9,0x28,0x08,0x00,0x00,
0x05,0x20,0x71,0x68,0x1b,0xc7,0x48,0x8f,0xe9,0xa3,0x53,0x29,0xb6,0x23,0x5a,0x25,0x02,0x45,0x72,0xca,0xa1,0xb6,0x06,0xfc,0xda,0x65,0x71,0x37,0xe6,0xd9,0x30,0x81,0x02,0xfe,0x00,0x00,
- 0x05,0x20,0x72,0x8b,0x72,0x38,0xfe,0x44,0xe9,0xc2,0xf4,0xbc,0xd8,0xce,0x1c,0xd5,0x50,0xb1,0x63,0x56,0x74,0x5e,0xf2,0xd3,0xe5,0xd5,0x29,0xe4,0x34,0x1d,0xf5,0x1c,0x7a,0xb9,0x00,0x00,
0x05,0x20,0x76,0x74,0x80,0x6c,0xab,0x7b,0x36,0x3a,0x6a,0x78,0xa4,0xa8,0xb8,0xb7,0xe7,0xf9,0x34,0x47,0x6d,0x34,0xca,0xa2,0xc6,0xef,0x81,0xab,0x62,0xb1,0x46,0x86,0xaf,0xd0,0x00,0x00,
+ 0x05,0x20,0x7b,0x8e,0x51,0x44,0xa2,0xfb,0xe2,0xde,0x06,0xad,0xe5,0xe5,0x3b,0x79,0xb3,0xd9,0x3e,0x74,0xb0,0x3f,0x55,0x13,0xc1,0xa6,0x72,0x2c,0x25,0x25,0x58,0xec,0x3b,0x5a,0x00,0x00,
0x05,0x20,0x80,0xfc,0x95,0xc8,0x95,0x91,0x2f,0x6b,0x6e,0xc4,0x2b,0xe1,0x2f,0xa0,0xcb,0xb8,0x76,0x5f,0x6f,0x1c,0x27,0xb0,0x3a,0x2c,0xb9,0x8d,0x6c,0x55,0xbd,0x02,0x60,0xe0,0x00,0x00,
- 0x05,0x20,0x80,0xc6,0x6f,0xb3,0x18,0x5a,0x1a,0xde,0x4e,0xde,0x50,0xd2,0xc6,0x3f,0xc5,0x96,0x09,0x35,0x3a,0x4d,0x88,0x5f,0xa3,0x49,0x37,0xff,0xe6,0xc5,0x43,0x10,0xaf,0xa8,0x00,0x00,
+ 0x05,0x20,0x89,0x52,0xac,0x8f,0x35,0xe0,0xac,0xfb,0xbb,0xc7,0xcd,0x1a,0x24,0x0a,0xb4,0xc5,0x11,0xff,0x5a,0xf0,0xbe,0xc1,0xad,0xaf,0x0d,0x87,0xf4,0xa9,0xda,0x12,0xf8,0x12,0x00,0x00,
0x05,0x20,0x8a,0x32,0x54,0x37,0x12,0x24,0xb5,0x1d,0xba,0x3c,0x45,0x03,0x2e,0xda,0xfe,0xf1,0x87,0x8f,0x31,0xe5,0xfe,0xed,0xfa,0x38,0x25,0x00,0x99,0xa5,0xf4,0x51,0x02,0xf7,0x00,0x00,
- 0x05,0x20,0x97,0x4e,0x74,0xcd,0x8b,0x37,0x76,0x15,0x3b,0x6d,0xb5,0xa4,0xa0,0x4b,0xf7,0x34,0x98,0x48,0x67,0x1e,0x99,0xe7,0x24,0xf4,0x00,0xf7,0x06,0x9c,0x45,0x69,0x34,0x01,0x00,0x00,
0x05,0x20,0x91,0x06,0xd1,0x9e,0xbd,0xab,0xc4,0x61,0xb3,0x0a,0xc2,0x3b,0x29,0xf3,0x10,0x38,0xee,0xbd,0x9d,0xe3,0x99,0x97,0x30,0x70,0x6e,0xe6,0xfb,0x6a,0x3c,0x07,0x3d,0xfd,0x00,0x00,
- 0x05,0x20,0x9c,0x97,0xc1,0xad,0xf4,0xd2,0x07,0xae,0xe8,0x3e,0x14,0x42,0x36,0x85,0x71,0xa0,0xa7,0xef,0x72,0x44,0xf1,0x74,0x8b,0x6f,0xa5,0xa4,0x2c,0x0d,0xcc,0x9f,0xb0,0x9d,0x00,0x00,
0x05,0x20,0x9d,0x0d,0x0f,0x58,0x1a,0x5c,0xb4,0x1a,0xeb,0xef,0x8e,0x91,0x8d,0x8c,0x1b,0x57,0x5d,0x6d,0x97,0x24,0x28,0x45,0x54,0x8a,0x3a,0xd5,0x05,0xfb,0x76,0xac,0x25,0x52,0x00,0x00,
0x05,0x20,0xa4,0xb3,0x30,0x54,0x28,0x0f,0xfb,0xe5,0x76,0xb0,0x31,0xb2,0x65,0x62,0x56,0x72,0x7c,0xc9,0xcd,0x07,0x4d,0x5f,0xb1,0x69,0xe0,0xf7,0x35,0x3d,0x30,0x3c,0x7d,0x64,0x00,0x00,
0x05,0x20,0xa9,0xa9,0xe5,0xae,0x01,0xc2,0x5e,0x76,0x2f,0x5d,0xa3,0x07,0xdc,0xce,0xb8,0xbc,0x6f,0x47,0xaf,0x3a,0x37,0xf8,0x5c,0x86,0xff,0xe9,0xb6,0xa5,0x00,0x93,0x76,0x11,0x00,0x00,
0x05,0x20,0xb2,0x63,0x45,0xf5,0x36,0xb0,0x79,0x58,0x0d,0x8a,0x54,0x52,0x16,0x2f,0x1f,0x74,0x93,0xe0,0x30,0x82,0x1b,0xe4,0x01,0x76,0xf5,0x03,0xa1,0x19,0xa3,0x8d,0x0e,0xce,0x00,0x00,
- 0x05,0x20,0xb5,0x55,0x31,0x3f,0xe7,0xc7,0x17,0xe4,0x31,0x87,0x47,0x45,0x7c,0x67,0x43,0x5c,0x82,0x73,0xd6,0x62,0x64,0x94,0x92,0x32,0x2d,0x81,0x0e,0x01,0x35,0xc0,0x7e,0xb7,0x00,0x00,
0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x00,0x00,
- 0x05,0x20,0xba,0xe0,0xd1,0xe5,0x2e,0x27,0x5b,0x1d,0x36,0x57,0x77,0xaf,0x64,0x04,0xfc,0xe1,0x8f,0x8c,0xf1,0x25,0x81,0x2b,0x4f,0x6a,0xf8,0x55,0x48,0xc2,0x5e,0x6f,0x62,0x43,0x00,0x00,
+ 0x05,0x20,0xb9,0x54,0x08,0xb2,0xaa,0xe6,0xea,0x55,0x16,0xa7,0x51,0x0f,0x8f,0xa1,0xc9,0xd0,0x7d,0x69,0x94,0x6b,0x26,0x6a,0xc5,0x81,0x41,0x5e,0x77,0x0e,0x47,0xec,0x10,0x8b,0x00,0x00,
0x05,0x20,0xc0,0xb9,0x7b,0x21,0xbd,0xa2,0x48,0xda,0x8a,0x3e,0xc3,0x6c,0xac,0xfd,0x6d,0x63,0x21,0xb6,0xb3,0x37,0xa9,0x4d,0x42,0x2c,0x9e,0x75,0x61,0x07,0xdc,0xc9,0xab,0x9b,0x00,0x00,
0x05,0x20,0xc8,0xdc,0x00,0xc8,0xdf,0xa1,0xb2,0xe9,0x9f,0x00,0xb3,0x86,0x93,0xc3,0xbc,0x6d,0x56,0xe2,0x83,0xfc,0xf4,0x6e,0x55,0x5d,0xed,0x4e,0x53,0xe6,0xd1,0x4c,0x38,0x3c,0x00,0x00,
0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x00,0x00,
0x05,0x20,0xce,0x25,0x15,0xbd,0x08,0xe9,0x67,0x1c,0xa2,0xa5,0x16,0x0e,0x38,0xd9,0xe4,0xc6,0x20,0x31,0x86,0x23,0x21,0x5a,0x5a,0x76,0x1e,0x74,0xd5,0xd3,0x4e,0x86,0x61,0xf4,0x00,0x00,
0x06,0x10,0xfc,0x32,0x17,0xea,0xe4,0x15,0xc3,0xbf,0x98,0x08,0x14,0x9d,0xb5,0xa2,0xc9,0xaa,0x20,0x8d,
0x06,0x10,0xfc,0xc7,0xbe,0x49,0xcc,0xd1,0xdc,0x91,0x31,0x25,0xf0,0xda,0x45,0x7d,0x08,0xce,0x20,0x8d,
+ 0x06,0x10,0xfc,0xdc,0x73,0xae,0xb1,0xa9,0x1b,0xf8,0xd4,0xc2,0x08,0x11,0xa4,0xc7,0xc3,0x4e,0x20,0x8d,
};
static const uint8_t chainparams_seed_test[] = {
diff --git a/src/coins.cpp b/src/coins.cpp
index 5a6ae525a7..0fe642e46b 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -34,7 +34,7 @@ size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
CCoinsViewBacked(baseIn), m_deterministic(deterministic),
- cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic))
+ cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
{}
size_t CCoinsViewCache::DynamicMemoryUsage() const {
@@ -253,9 +253,12 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/true);
- if (fOk && !cacheCoins.empty()) {
- /* BatchWrite must erase all cacheCoins elements when erase=true. */
- throw std::logic_error("Not all cached coins were erased");
+ if (fOk) {
+ if (!cacheCoins.empty()) {
+ /* BatchWrite must erase all cacheCoins elements when erase=true. */
+ throw std::logic_error("Not all cached coins were erased");
+ }
+ ReallocateCache();
}
cachedCoinsUsage = 0;
return fOk;
@@ -314,7 +317,9 @@ void CCoinsViewCache::ReallocateCache()
// Cache should be empty when we're calling this.
assert(cacheCoins.size() == 0);
cacheCoins.~CCoinsMap();
- ::new (&cacheCoins) CCoinsMap(0, SaltedOutpointHasher(/*deterministic=*/m_deterministic));
+ m_cache_coins_memory_resource.~CCoinsMapMemoryResource();
+ ::new (&m_cache_coins_memory_resource) CCoinsMapMemoryResource{};
+ ::new (&cacheCoins) CCoinsMap{0, SaltedOutpointHasher{/*deterministic=*/m_deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource};
}
void CCoinsViewCache::SanityCheck() const
diff --git a/src/coins.h b/src/coins.h
index dd336b210a..039a07054d 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -11,6 +11,7 @@
#include <memusage.h>
#include <primitives/transaction.h>
#include <serialize.h>
+#include <support/allocators/pool.h>
#include <uint256.h>
#include <util/hasher.h>
@@ -131,7 +132,23 @@ struct CCoinsCacheEntry
CCoinsCacheEntry(Coin&& coin_, unsigned char flag) : coin(std::move(coin_)), flags(flag) {}
};
-typedef std::unordered_map<COutPoint, CCoinsCacheEntry, SaltedOutpointHasher> CCoinsMap;
+/**
+ * PoolAllocator's MAX_BLOCK_SIZE_BYTES parameter here uses sizeof the data, and adds the size
+ * of 4 pointers. We do not know the exact node size used in the std::unordered_node implementation
+ * because it is implementation defined. Most implementations have an overhead of 1 or 2 pointers,
+ * so nodes can be connected in a linked list, and in some cases the hash value is stored as well.
+ * Using an additional sizeof(void*)*4 for MAX_BLOCK_SIZE_BYTES should thus be sufficient so that
+ * all implementations can allocate the nodes from the PoolAllocator.
+ */
+using CCoinsMap = std::unordered_map<COutPoint,
+ CCoinsCacheEntry,
+ SaltedOutpointHasher,
+ std::equal_to<COutPoint>,
+ PoolAllocator<std::pair<const COutPoint, CCoinsCacheEntry>,
+ sizeof(std::pair<const COutPoint, CCoinsCacheEntry>) + sizeof(void*) * 4,
+ alignof(void*)>>;
+
+using CCoinsMapMemoryResource = CCoinsMap::allocator_type::ResourceType;
/** Cursor for iterating over CoinsView state */
class CCoinsViewCursor
@@ -220,6 +237,7 @@ protected:
* declared as "const".
*/
mutable uint256 hashBlock;
+ mutable CCoinsMapMemoryResource m_cache_coins_memory_resource{};
mutable CCoinsMap cacheCoins;
/* Cached dynamic memory usage for the inner Coin objects. */
diff --git a/src/common/args.cpp b/src/common/args.cpp
new file mode 100644
index 0000000000..d29b8648bf
--- /dev/null
+++ b/src/common/args.cpp
@@ -0,0 +1,823 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2022 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 <common/args.h>
+
+#include <chainparamsbase.h>
+#include <logging.h>
+#include <sync.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/check.h>
+#include <util/fs.h>
+#include <util/fs_helpers.h>
+#include <util/settings.h>
+#include <util/strencodings.h>
+
+#ifdef WIN32
+#include <codecvt> /* for codecvt_utf8_utf16 */
+#include <shellapi.h> /* for CommandLineToArgvW */
+#include <shlobj.h> /* for CSIDL_APPDATA */
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <filesystem>
+#include <map>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
+const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
+
+ArgsManager gArgs;
+
+/**
+ * Interpret a string argument as a boolean.
+ *
+ * The definition of LocaleIndependentAtoi<int>() requires that non-numeric string values
+ * like "foo", return 0. This means that if a user unintentionally supplies a
+ * non-integer argument here, the return value is always false. This means that
+ * -foo=false does what the user probably expects, but -foo=true is well defined
+ * but does not do what they probably expected.
+ *
+ * The return value of LocaleIndependentAtoi<int>(...) is zero when given input not
+ * representable as an int.
+ *
+ * For a more extensive discussion of this topic (and a wide range of opinions
+ * on the Right Way to change this code), see PR12713.
+ */
+static bool InterpretBool(const std::string& strValue)
+{
+ if (strValue.empty())
+ return true;
+ return (LocaleIndependentAtoi<int>(strValue) != 0);
+}
+
+static std::string SettingName(const std::string& arg)
+{
+ return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
+}
+
+/**
+ * Parse "name", "section.name", "noname", "section.noname" settings keys.
+ *
+ * @note Where an option was negated can be later checked using the
+ * IsArgNegated() method. One use case for this is to have a way to disable
+ * options that are not normally boolean (e.g. using -nodebuglogfile to request
+ * that debug log output is not sent to any file at all).
+ */
+KeyInfo InterpretKey(std::string key)
+{
+ KeyInfo result;
+ // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
+ size_t option_index = key.find('.');
+ if (option_index != std::string::npos) {
+ result.section = key.substr(0, option_index);
+ key.erase(0, option_index + 1);
+ }
+ if (key.substr(0, 2) == "no") {
+ key.erase(0, 2);
+ result.negated = true;
+ }
+ result.name = key;
+ return result;
+}
+
+/**
+ * Interpret settings value based on registered flags.
+ *
+ * @param[in] key key information to know if key was negated
+ * @param[in] value string value of setting to be parsed
+ * @param[in] flags ArgsManager registered argument flags
+ * @param[out] error Error description if settings value is not valid
+ *
+ * @return parsed settings value if it is valid, otherwise nullopt accompanied
+ * by a descriptive error string
+ */
+std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
+ unsigned int flags, std::string& error)
+{
+ // Return negated settings as false values.
+ if (key.negated) {
+ if (flags & ArgsManager::DISALLOW_NEGATION) {
+ error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
+ return std::nullopt;
+ }
+ // Double negatives like -nofoo=0 are supported (but discouraged)
+ if (value && !InterpretBool(*value)) {
+ LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
+ return true;
+ }
+ return false;
+ }
+ if (!value && (flags & ArgsManager::DISALLOW_ELISION)) {
+ error = strprintf("Can not set -%s with no value. Please specify value with -%s=value.", key.name, key.name);
+ return std::nullopt;
+ }
+ return value ? *value : "";
+}
+
+// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
+// #include class definitions for all members.
+// For example, m_settings has an internal dependency on univalue.
+ArgsManager::ArgsManager() = default;
+ArgsManager::~ArgsManager() = default;
+
+std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
+{
+ std::set<std::string> unsuitables;
+
+ LOCK(cs_args);
+
+ // if there's no section selected, don't worry
+ if (m_network.empty()) return std::set<std::string> {};
+
+ // if it's okay to use the default section for this network, don't worry
+ if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
+
+ for (const auto& arg : m_network_only_args) {
+ if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
+ unsuitables.insert(arg);
+ }
+ }
+ return unsuitables;
+}
+
+std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
+{
+ // Section names to be recognized in the config file.
+ static const std::set<std::string> available_sections{
+ CBaseChainParams::REGTEST,
+ CBaseChainParams::SIGNET,
+ CBaseChainParams::TESTNET,
+ CBaseChainParams::MAIN
+ };
+
+ LOCK(cs_args);
+ std::list<SectionInfo> unrecognized = m_config_sections;
+ unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
+ return unrecognized;
+}
+
+void ArgsManager::SelectConfigNetwork(const std::string& network)
+{
+ LOCK(cs_args);
+ m_network = network;
+}
+
+bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
+{
+ LOCK(cs_args);
+ m_settings.command_line_options.clear();
+
+ for (int i = 1; i < argc; i++) {
+ std::string key(argv[i]);
+
+#ifdef MAC_OSX
+ // At the first time when a user gets the "App downloaded from the
+ // internet" warning, and clicks the Open button, macOS passes
+ // a unique process serial number (PSN) as -psn_... command-line
+ // argument, which we filter out.
+ if (key.substr(0, 5) == "-psn_") continue;
+#endif
+
+ if (key == "-") break; //bitcoin-tx using stdin
+ std::optional<std::string> val;
+ size_t is_index = key.find('=');
+ if (is_index != std::string::npos) {
+ val = key.substr(is_index + 1);
+ key.erase(is_index);
+ }
+#ifdef WIN32
+ key = ToLower(key);
+ if (key[0] == '/')
+ key[0] = '-';
+#endif
+
+ if (key[0] != '-') {
+ if (!m_accept_any_command && m_command.empty()) {
+ // The first non-dash arg is a registered command
+ std::optional<unsigned int> flags = GetArgFlags(key);
+ if (!flags || !(*flags & ArgsManager::COMMAND)) {
+ error = strprintf("Invalid command '%s'", argv[i]);
+ return false;
+ }
+ }
+ m_command.push_back(key);
+ while (++i < argc) {
+ // The remaining args are command args
+ m_command.push_back(argv[i]);
+ }
+ break;
+ }
+
+ // Transform --foo to -foo
+ if (key.length() > 1 && key[1] == '-')
+ key.erase(0, 1);
+
+ // Transform -foo to foo
+ key.erase(0, 1);
+ KeyInfo keyinfo = InterpretKey(key);
+ std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
+
+ // Unknown command line options and command line options with dot
+ // characters (which are returned from InterpretKey with nonempty
+ // section strings) are not valid.
+ if (!flags || !keyinfo.section.empty()) {
+ error = strprintf("Invalid parameter %s", argv[i]);
+ return false;
+ }
+
+ std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
+ if (!value) return false;
+
+ m_settings.command_line_options[keyinfo.name].push_back(*value);
+ }
+
+ // we do not allow -includeconf from command line, only -noincludeconf
+ if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
+ const util::SettingsSpan values{*includes};
+ // Range may be empty if -noincludeconf was passed
+ if (!values.empty()) {
+ error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
+ return false; // pick first value as example
+ }
+ }
+ return true;
+}
+
+std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
+{
+ LOCK(cs_args);
+ for (const auto& arg_map : m_available_args) {
+ const auto search = arg_map.second.find(name);
+ if (search != arg_map.second.end()) {
+ return search->second.m_flags;
+ }
+ }
+ return std::nullopt;
+}
+
+fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
+{
+ if (IsArgNegated(arg)) return fs::path{};
+ std::string path_str = GetArg(arg, "");
+ if (path_str.empty()) return default_value;
+ fs::path result = fs::PathFromString(path_str).lexically_normal();
+ // Remove trailing slash, if present.
+ return result.has_filename() ? result : result.parent_path();
+}
+
+const fs::path& ArgsManager::GetBlocksDirPath() const
+{
+ LOCK(cs_args);
+ fs::path& path = m_cached_blocks_path;
+
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
+
+ if (IsArgSet("-blocksdir")) {
+ path = fs::absolute(GetPathArg("-blocksdir"));
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDataDirBase();
+ }
+
+ path /= fs::PathFromString(BaseParams().DataDir());
+ path /= "blocks";
+ fs::create_directories(path);
+ return path;
+}
+
+const fs::path& ArgsManager::GetDataDir(bool net_specific) const
+{
+ LOCK(cs_args);
+ fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
+
+ // Used cached path if available
+ if (!path.empty()) return path;
+
+ const fs::path datadir{GetPathArg("-datadir")};
+ if (!datadir.empty()) {
+ path = fs::absolute(datadir);
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDefaultDataDir();
+ }
+
+ if (net_specific && !BaseParams().DataDir().empty()) {
+ path /= fs::PathFromString(BaseParams().DataDir());
+ }
+
+ return path;
+}
+
+void ArgsManager::ClearPathCache()
+{
+ LOCK(cs_args);
+
+ m_cached_datadir_path = fs::path();
+ m_cached_network_datadir_path = fs::path();
+ m_cached_blocks_path = fs::path();
+}
+
+std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
+{
+ Command ret;
+ LOCK(cs_args);
+ auto it = m_command.begin();
+ if (it == m_command.end()) {
+ // No command was passed
+ return std::nullopt;
+ }
+ if (!m_accept_any_command) {
+ // The registered command
+ ret.command = *(it++);
+ }
+ while (it != m_command.end()) {
+ // The unregistered command and args (if any)
+ ret.args.push_back(*(it++));
+ }
+ return ret;
+}
+
+std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
+{
+ std::vector<std::string> result;
+ for (const util::SettingsValue& value : GetSettingsList(strArg)) {
+ result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
+ }
+ return result;
+}
+
+bool ArgsManager::IsArgSet(const std::string& strArg) const
+{
+ return !GetSetting(strArg).isNull();
+}
+
+bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
+{
+ fs::path settings = GetPathArg("-settings", BITCOIN_SETTINGS_FILENAME);
+ if (settings.empty()) {
+ return false;
+ }
+ if (backup) {
+ settings += ".bak";
+ }
+ if (filepath) {
+ *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
+ }
+ return true;
+}
+
+static void SaveErrors(const std::vector<std::string> errors, std::vector<std::string>* error_out)
+{
+ for (const auto& error : errors) {
+ if (error_out) {
+ error_out->emplace_back(error);
+ } else {
+ LogPrintf("%s\n", error);
+ }
+ }
+}
+
+bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
+{
+ fs::path path;
+ if (!GetSettingsPath(&path, /* temp= */ false)) {
+ return true; // Do nothing if settings file disabled.
+ }
+
+ LOCK(cs_args);
+ m_settings.rw_settings.clear();
+ std::vector<std::string> read_errors;
+ if (!util::ReadSettings(path, m_settings.rw_settings, read_errors)) {
+ SaveErrors(read_errors, errors);
+ return false;
+ }
+ for (const auto& setting : m_settings.rw_settings) {
+ KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
+ if (!GetArgFlags('-' + key.name)) {
+ LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
+ }
+ }
+ return true;
+}
+
+bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
+{
+ fs::path path, path_tmp;
+ if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
+ throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
+ }
+
+ LOCK(cs_args);
+ std::vector<std::string> write_errors;
+ if (!util::WriteSettings(path_tmp, m_settings.rw_settings, write_errors)) {
+ SaveErrors(write_errors, errors);
+ return false;
+ }
+ if (!RenameOver(path_tmp, path)) {
+ SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
+ return false;
+ }
+ return true;
+}
+
+util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
+{
+ LOCK(cs_args);
+ return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
+ /*ignore_nonpersistent=*/true, /*get_chain_name=*/false);
+}
+
+bool ArgsManager::IsArgNegated(const std::string& strArg) const
+{
+ return GetSetting(strArg).isFalse();
+}
+
+std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
+{
+ return GetArg(strArg).value_or(strDefault);
+}
+
+std::optional<std::string> ArgsManager::GetArg(const std::string& strArg) const
+{
+ const util::SettingsValue value = GetSetting(strArg);
+ return SettingToString(value);
+}
+
+std::optional<std::string> SettingToString(const util::SettingsValue& value)
+{
+ if (value.isNull()) return std::nullopt;
+ if (value.isFalse()) return "0";
+ if (value.isTrue()) return "1";
+ if (value.isNum()) return value.getValStr();
+ return value.get_str();
+}
+
+std::string SettingToString(const util::SettingsValue& value, const std::string& strDefault)
+{
+ return SettingToString(value).value_or(strDefault);
+}
+
+int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
+{
+ return GetIntArg(strArg).value_or(nDefault);
+}
+
+std::optional<int64_t> ArgsManager::GetIntArg(const std::string& strArg) const
+{
+ const util::SettingsValue value = GetSetting(strArg);
+ return SettingToInt(value);
+}
+
+std::optional<int64_t> SettingToInt(const util::SettingsValue& value)
+{
+ if (value.isNull()) return std::nullopt;
+ if (value.isFalse()) return 0;
+ if (value.isTrue()) return 1;
+ if (value.isNum()) return value.getInt<int64_t>();
+ return LocaleIndependentAtoi<int64_t>(value.get_str());
+}
+
+int64_t SettingToInt(const util::SettingsValue& value, int64_t nDefault)
+{
+ return SettingToInt(value).value_or(nDefault);
+}
+
+bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
+{
+ return GetBoolArg(strArg).value_or(fDefault);
+}
+
+std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
+{
+ const util::SettingsValue value = GetSetting(strArg);
+ return SettingToBool(value);
+}
+
+std::optional<bool> SettingToBool(const util::SettingsValue& value)
+{
+ if (value.isNull()) return std::nullopt;
+ if (value.isBool()) return value.get_bool();
+ return InterpretBool(value.get_str());
+}
+
+bool SettingToBool(const util::SettingsValue& value, bool fDefault)
+{
+ return SettingToBool(value).value_or(fDefault);
+}
+
+bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
+{
+ LOCK(cs_args);
+ if (IsArgSet(strArg)) return false;
+ ForceSetArg(strArg, strValue);
+ return true;
+}
+
+bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
+{
+ if (fValue)
+ return SoftSetArg(strArg, std::string("1"));
+ else
+ return SoftSetArg(strArg, std::string("0"));
+}
+
+void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
+{
+ LOCK(cs_args);
+ m_settings.forced_settings[SettingName(strArg)] = strValue;
+}
+
+void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
+{
+ Assert(cmd.find('=') == std::string::npos);
+ Assert(cmd.at(0) != '-');
+
+ LOCK(cs_args);
+ m_accept_any_command = false; // latch to false
+ std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
+ auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
+ Assert(ret.second); // Fail on duplicate commands
+}
+
+void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
+{
+ Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
+
+ // Split arg name from its help param
+ size_t eq_index = name.find('=');
+ if (eq_index == std::string::npos) {
+ eq_index = name.size();
+ }
+ std::string arg_name = name.substr(0, eq_index);
+
+ LOCK(cs_args);
+ std::map<std::string, Arg>& arg_map = m_available_args[cat];
+ auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
+ assert(ret.second); // Make sure an insertion actually happened
+
+ if (flags & ArgsManager::NETWORK_ONLY) {
+ m_network_only_args.emplace(arg_name);
+ }
+}
+
+void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
+{
+ for (const std::string& name : names) {
+ AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
+ }
+}
+
+std::string ArgsManager::GetHelpMessage() const
+{
+ const bool show_debug = GetBoolArg("-help-debug", false);
+
+ std::string usage;
+ LOCK(cs_args);
+ for (const auto& arg_map : m_available_args) {
+ switch(arg_map.first) {
+ case OptionsCategory::OPTIONS:
+ usage += HelpMessageGroup("Options:");
+ break;
+ case OptionsCategory::CONNECTION:
+ usage += HelpMessageGroup("Connection options:");
+ break;
+ case OptionsCategory::ZMQ:
+ usage += HelpMessageGroup("ZeroMQ notification options:");
+ break;
+ case OptionsCategory::DEBUG_TEST:
+ usage += HelpMessageGroup("Debugging/Testing options:");
+ break;
+ case OptionsCategory::NODE_RELAY:
+ usage += HelpMessageGroup("Node relay options:");
+ break;
+ case OptionsCategory::BLOCK_CREATION:
+ usage += HelpMessageGroup("Block creation options:");
+ break;
+ case OptionsCategory::RPC:
+ usage += HelpMessageGroup("RPC server options:");
+ break;
+ case OptionsCategory::WALLET:
+ usage += HelpMessageGroup("Wallet options:");
+ break;
+ case OptionsCategory::WALLET_DEBUG_TEST:
+ if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
+ break;
+ case OptionsCategory::CHAINPARAMS:
+ usage += HelpMessageGroup("Chain selection options:");
+ break;
+ case OptionsCategory::GUI:
+ usage += HelpMessageGroup("UI Options:");
+ break;
+ case OptionsCategory::COMMANDS:
+ usage += HelpMessageGroup("Commands:");
+ break;
+ case OptionsCategory::REGISTER_COMMANDS:
+ usage += HelpMessageGroup("Register Commands:");
+ break;
+ default:
+ break;
+ }
+
+ // When we get to the hidden options, stop
+ if (arg_map.first == OptionsCategory::HIDDEN) break;
+
+ for (const auto& arg : arg_map.second) {
+ if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
+ std::string name;
+ if (arg.second.m_help_param.empty()) {
+ name = arg.first;
+ } else {
+ name = arg.first + arg.second.m_help_param;
+ }
+ usage += HelpMessageOpt(name, arg.second.m_help_text);
+ }
+ }
+ }
+ return usage;
+}
+
+bool HelpRequested(const ArgsManager& args)
+{
+ return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
+}
+
+void SetupHelpOptions(ArgsManager& args)
+{
+ args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ args.AddHiddenArgs({"-h", "-help"});
+}
+
+static const int screenWidth = 79;
+static const int optIndent = 2;
+static const int msgIndent = 7;
+
+std::string HelpMessageGroup(const std::string &message) {
+ return std::string(message) + std::string("\n\n");
+}
+
+std::string HelpMessageOpt(const std::string &option, const std::string &message) {
+ return std::string(optIndent,' ') + std::string(option) +
+ std::string("\n") + std::string(msgIndent,' ') +
+ FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
+ std::string("\n\n");
+}
+
+fs::path GetDefaultDataDir()
+{
+ // Windows: C:\Users\Username\AppData\Roaming\Bitcoin
+ // macOS: ~/Library/Application Support/Bitcoin
+ // Unix-like: ~/.bitcoin
+#ifdef WIN32
+ // Windows
+ return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
+#else
+ fs::path pathRet;
+ char* pszHome = getenv("HOME");
+ if (pszHome == nullptr || strlen(pszHome) == 0)
+ pathRet = fs::path("/");
+ else
+ pathRet = fs::path(pszHome);
+#ifdef MAC_OSX
+ // macOS
+ return pathRet / "Library/Application Support/Bitcoin";
+#else
+ // Unix-like
+ return pathRet / ".bitcoin";
+#endif
+#endif
+}
+
+bool CheckDataDirOption(const ArgsManager& args)
+{
+ const fs::path datadir{args.GetPathArg("-datadir")};
+ return datadir.empty() || fs::is_directory(fs::absolute(datadir));
+}
+
+fs::path ArgsManager::GetConfigFilePath() const
+{
+ return GetConfigFile(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME));
+}
+
+std::string ArgsManager::GetChainName() const
+{
+ auto get_net = [&](const std::string& arg) {
+ LOCK(cs_args);
+ util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
+ /* ignore_default_section_config= */ false,
+ /*ignore_nonpersistent=*/false,
+ /* get_chain_name= */ true);
+ return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
+ };
+
+ const bool fRegTest = get_net("-regtest");
+ const bool fSigNet = get_net("-signet");
+ const bool fTestNet = get_net("-testnet");
+ const bool is_chain_arg_set = IsArgSet("-chain");
+
+ if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) {
+ throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one.");
+ }
+ if (fRegTest)
+ return CBaseChainParams::REGTEST;
+ if (fSigNet) {
+ return CBaseChainParams::SIGNET;
+ }
+ if (fTestNet)
+ return CBaseChainParams::TESTNET;
+
+ return GetArg("-chain", CBaseChainParams::MAIN);
+}
+
+bool ArgsManager::UseDefaultSection(const std::string& arg) const
+{
+ return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0;
+}
+
+util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
+{
+ LOCK(cs_args);
+ return util::GetSetting(
+ m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
+ /*ignore_nonpersistent=*/false, /*get_chain_name=*/false);
+}
+
+std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
+{
+ LOCK(cs_args);
+ return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
+}
+
+void ArgsManager::logArgsPrefix(
+ const std::string& prefix,
+ const std::string& section,
+ const std::map<std::string, std::vector<util::SettingsValue>>& args) const
+{
+ std::string section_str = section.empty() ? "" : "[" + section + "] ";
+ for (const auto& arg : args) {
+ for (const auto& value : arg.second) {
+ std::optional<unsigned int> flags = GetArgFlags('-' + arg.first);
+ if (flags) {
+ std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
+ LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
+ }
+ }
+ }
+}
+
+void ArgsManager::LogArgs() const
+{
+ LOCK(cs_args);
+ for (const auto& section : m_settings.ro_config) {
+ logArgsPrefix("Config file arg:", section.first, section.second);
+ }
+ for (const auto& setting : m_settings.rw_settings) {
+ LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
+ }
+ logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
+}
+
+namespace common {
+#ifdef WIN32
+WinCmdLineArgs::WinCmdLineArgs()
+{
+ wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
+ argv = new char*[argc];
+ args.resize(argc);
+ for (int i = 0; i < argc; i++) {
+ args[i] = utf8_cvt.to_bytes(wargv[i]);
+ argv[i] = &*args[i].begin();
+ }
+ LocalFree(wargv);
+}
+
+WinCmdLineArgs::~WinCmdLineArgs()
+{
+ delete[] argv;
+}
+
+std::pair<int, char**> WinCmdLineArgs::get()
+{
+ return std::make_pair(argc, argv);
+}
+#endif
+} // namespace common
diff --git a/src/common/args.h b/src/common/args.h
new file mode 100644
index 0000000000..430c392e2b
--- /dev/null
+++ b/src/common/args.h
@@ -0,0 +1,465 @@
+// Copyright (c) 2023 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_COMMON_ARGS_H
+#define BITCOIN_COMMON_ARGS_H
+
+#include <compat/compat.h>
+#include <sync.h>
+#include <util/fs.h>
+#include <util/settings.h>
+
+#include <iosfwd>
+#include <list>
+#include <map>
+#include <optional>
+#include <set>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+class ArgsManager;
+
+extern const char * const BITCOIN_CONF_FILENAME;
+extern const char * const BITCOIN_SETTINGS_FILENAME;
+
+// Return true if -datadir option points to a valid directory or is not specified.
+bool CheckDataDirOption(const ArgsManager& args);
+fs::path GetConfigFile(const ArgsManager& args, const fs::path& configuration_file_path);
+
+/**
+ * Most paths passed as configuration arguments are treated as relative to
+ * the datadir if they are not absolute.
+ *
+ * @param args Parsed arguments and settings.
+ * @param path The path to be conditionally prefixed with datadir.
+ * @param net_specific Use network specific datadir variant
+ * @return The normalized path.
+ */
+fs::path AbsPathForConfigVal(const ArgsManager& args, const fs::path& path, bool net_specific = true);
+
+inline bool IsSwitchChar(char c)
+{
+#ifdef WIN32
+ return c == '-' || c == '/';
+#else
+ return c == '-';
+#endif
+}
+
+enum class OptionsCategory {
+ OPTIONS,
+ CONNECTION,
+ WALLET,
+ WALLET_DEBUG_TEST,
+ ZMQ,
+ DEBUG_TEST,
+ CHAINPARAMS,
+ NODE_RELAY,
+ BLOCK_CREATION,
+ RPC,
+ GUI,
+ COMMANDS,
+ REGISTER_COMMANDS,
+
+ HIDDEN // Always the last option to avoid printing these in the help
+};
+
+struct KeyInfo {
+ std::string name;
+ std::string section;
+ bool negated{false};
+};
+
+KeyInfo InterpretKey(std::string key);
+
+std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
+ unsigned int flags, std::string& error);
+
+struct SectionInfo {
+ std::string m_name;
+ std::string m_file;
+ int m_line;
+};
+
+std::string SettingToString(const util::SettingsValue&, const std::string&);
+std::optional<std::string> SettingToString(const util::SettingsValue&);
+
+int64_t SettingToInt(const util::SettingsValue&, int64_t);
+std::optional<int64_t> SettingToInt(const util::SettingsValue&);
+
+bool SettingToBool(const util::SettingsValue&, bool);
+std::optional<bool> SettingToBool(const util::SettingsValue&);
+
+class ArgsManager
+{
+public:
+ /**
+ * Flags controlling how config and command line arguments are validated and
+ * interpreted.
+ */
+ enum Flags : uint32_t {
+ ALLOW_ANY = 0x01, //!< disable validation
+ // ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
+ // ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
+ // ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
+ // ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
+ DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
+ DISALLOW_ELISION = 0x40, //!< disallow -foo syntax that doesn't assign any value
+
+ DEBUG_ONLY = 0x100,
+ /* Some options would cause cross-contamination if values for
+ * mainnet were used while running on regtest/testnet (or vice-versa).
+ * Setting them as NETWORK_ONLY ensures that sharing a config file
+ * between mainnet and regtest/testnet won't cause problems due to these
+ * parameters by accident. */
+ NETWORK_ONLY = 0x200,
+ // This argument's value is sensitive (such as a password).
+ SENSITIVE = 0x400,
+ COMMAND = 0x800,
+ };
+
+protected:
+ struct Arg
+ {
+ std::string m_help_param;
+ std::string m_help_text;
+ unsigned int m_flags;
+ };
+
+ mutable RecursiveMutex cs_args;
+ util::Settings m_settings GUARDED_BY(cs_args);
+ std::vector<std::string> m_command GUARDED_BY(cs_args);
+ std::string m_network GUARDED_BY(cs_args);
+ std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
+ std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
+ bool m_accept_any_command GUARDED_BY(cs_args){true};
+ std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
+ mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
+ mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
+ mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
+
+ [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
+
+ /**
+ * Returns true if settings values from the default section should be used,
+ * depending on the current network and whether the setting is
+ * network-specific.
+ */
+ bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
+
+ public:
+ /**
+ * Get setting value.
+ *
+ * Result will be null if setting was unset, true if "-setting" argument was passed
+ * false if "-nosetting" argument was passed, and a string if a "-setting=value"
+ * argument was passed.
+ */
+ util::SettingsValue GetSetting(const std::string& arg) const;
+
+ /**
+ * Get list of setting values.
+ */
+ std::vector<util::SettingsValue> GetSettingsList(const std::string& arg) const;
+
+ ArgsManager();
+ ~ArgsManager();
+
+ /**
+ * Select the network in use
+ */
+ void SelectConfigNetwork(const std::string& network);
+
+ [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
+
+ /**
+ * Return config file path (read-only)
+ */
+ fs::path GetConfigFilePath() const;
+ [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
+
+ /**
+ * Log warnings for options in m_section_only_args when
+ * they are specified in the default section but not overridden
+ * on the command line or in a network-specific section in the
+ * config file.
+ */
+ std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
+
+ /**
+ * Log warnings for unrecognized section names in the config file.
+ */
+ std::list<SectionInfo> GetUnrecognizedSections() const;
+
+ struct Command {
+ /** The command (if one has been registered with AddCommand), or empty */
+ std::string command;
+ /**
+ * If command is non-empty: Any args that followed it
+ * If command is empty: The unregistered command and any args that followed it
+ */
+ std::vector<std::string> args;
+ };
+ /**
+ * Get the command and command args (returns std::nullopt if no command provided)
+ */
+ std::optional<const Command> GetCommand() const;
+
+ /**
+ * Get blocks directory path
+ *
+ * @return Blocks path which is network specific
+ */
+ const fs::path& GetBlocksDirPath() const;
+
+ /**
+ * Get data directory path
+ *
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ */
+ const fs::path& GetDataDirBase() const { return GetDataDir(false); }
+
+ /**
+ * Get data directory path with appended network identifier
+ *
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ */
+ const fs::path& GetDataDirNet() const { return GetDataDir(true); }
+
+ /**
+ * Clear cached directory paths
+ */
+ void ClearPathCache();
+
+ /**
+ * Return a vector of strings of the given argument
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @return command-line arguments
+ */
+ std::vector<std::string> GetArgs(const std::string& strArg) const;
+
+ /**
+ * Return true if the given argument has been manually set
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @return true if the argument has been set
+ */
+ bool IsArgSet(const std::string& strArg) const;
+
+ /**
+ * Return true if the argument was originally passed as a negated option,
+ * i.e. -nofoo.
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @return true if the argument was passed negated
+ */
+ bool IsArgNegated(const std::string& strArg) const;
+
+ /**
+ * Return string argument or default value
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @param strDefault (e.g. "1")
+ * @return command-line argument or default value
+ */
+ std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
+ std::optional<std::string> GetArg(const std::string& strArg) const;
+
+ /**
+ * Return path argument or default value
+ *
+ * @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
+ * @param default_value Optional default value to return instead of the empty path.
+ * @return normalized path if argument is set, with redundant "." and ".."
+ * path components and trailing separators removed (see patharg unit test
+ * for examples or implementation for details). If argument is empty or not
+ * set, default_value is returned unchanged.
+ */
+ fs::path GetPathArg(std::string arg, const fs::path& default_value = {}) const;
+
+ /**
+ * Return integer argument or default value
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @param nDefault (e.g. 1)
+ * @return command-line argument (0 if invalid number) or default value
+ */
+ int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
+ std::optional<int64_t> GetIntArg(const std::string& strArg) const;
+
+ /**
+ * Return boolean argument or default value
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @param fDefault (true or false)
+ * @return command-line argument or default value
+ */
+ bool GetBoolArg(const std::string& strArg, bool fDefault) const;
+ std::optional<bool> GetBoolArg(const std::string& strArg) const;
+
+ /**
+ * Set an argument if it doesn't already have a value
+ *
+ * @param strArg Argument to set (e.g. "-foo")
+ * @param strValue Value (e.g. "1")
+ * @return true if argument gets set, false if it already had a value
+ */
+ bool SoftSetArg(const std::string& strArg, const std::string& strValue);
+
+ /**
+ * Set a boolean argument if it doesn't already have a value
+ *
+ * @param strArg Argument to set (e.g. "-foo")
+ * @param fValue Value (e.g. false)
+ * @return true if argument gets set, false if it already had a value
+ */
+ bool SoftSetBoolArg(const std::string& strArg, bool fValue);
+
+ // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
+ // been set. Also called directly in testing.
+ void ForceSetArg(const std::string& strArg, const std::string& strValue);
+
+ /**
+ * Returns the appropriate chain name from the program arguments.
+ * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given.
+ */
+ std::string GetChainName() const;
+
+ /**
+ * Add argument
+ */
+ void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
+
+ /**
+ * Add subcommand
+ */
+ void AddCommand(const std::string& cmd, const std::string& help);
+
+ /**
+ * Add many hidden arguments
+ */
+ void AddHiddenArgs(const std::vector<std::string>& args);
+
+ /**
+ * Clear available arguments
+ */
+ void ClearArgs() {
+ LOCK(cs_args);
+ m_available_args.clear();
+ m_network_only_args.clear();
+ }
+
+ /**
+ * Get the help string
+ */
+ std::string GetHelpMessage() const;
+
+ /**
+ * Return Flags for known arg.
+ * Return nullopt for unknown arg.
+ */
+ std::optional<unsigned int> GetArgFlags(const std::string& name) const;
+
+ /**
+ * Get settings file path, or return false if read-write settings were
+ * disabled with -nosettings.
+ */
+ bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const;
+
+ /**
+ * Read settings file. Push errors to vector, or log them if null.
+ */
+ bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
+
+ /**
+ * Write settings file or backup settings file. Push errors to vector, or
+ * log them if null.
+ */
+ bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const;
+
+ /**
+ * Get current setting from config file or read/write settings file,
+ * ignoring nonpersistent command line or forced settings values.
+ */
+ util::SettingsValue GetPersistentSetting(const std::string& name) const;
+
+ /**
+ * Access settings with lock held.
+ */
+ template <typename Fn>
+ void LockSettings(Fn&& fn)
+ {
+ LOCK(cs_args);
+ fn(m_settings);
+ }
+
+ /**
+ * Log the config file options and the command line arguments,
+ * useful for troubleshooting.
+ */
+ void LogArgs() const;
+
+private:
+ /**
+ * Get data directory path
+ *
+ * @param net_specific Append network identifier to the returned path
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ */
+ const fs::path& GetDataDir(bool net_specific) const;
+
+ // Helper function for LogArgs().
+ void logArgsPrefix(
+ const std::string& prefix,
+ const std::string& section,
+ const std::map<std::string, std::vector<util::SettingsValue>>& args) const;
+};
+
+extern ArgsManager gArgs;
+
+/**
+ * @return true if help has been requested via a command-line arg
+ */
+bool HelpRequested(const ArgsManager& args);
+
+/** Add help options to the args manager */
+void SetupHelpOptions(ArgsManager& args);
+
+/**
+ * Format a string to be used as group of options in help messages
+ *
+ * @param message Group name (e.g. "RPC server options:")
+ * @return the formatted string
+ */
+std::string HelpMessageGroup(const std::string& message);
+
+/**
+ * Format a string to be used as option description in help messages
+ *
+ * @param option Option message (e.g. "-rpcuser=<user>")
+ * @param message Option description (e.g. "Username for JSON-RPC connections")
+ * @return the formatted string
+ */
+std::string HelpMessageOpt(const std::string& option, const std::string& message);
+
+namespace common {
+#ifdef WIN32
+class WinCmdLineArgs
+{
+public:
+ WinCmdLineArgs();
+ ~WinCmdLineArgs();
+ std::pair<int, char**> get();
+
+private:
+ int argc;
+ char** argv;
+ std::vector<std::string> args;
+};
+#endif
+} // namespace common
+
+#endif // BITCOIN_COMMON_ARGS_H
diff --git a/src/common/config.cpp b/src/common/config.cpp
new file mode 100644
index 0000000000..747503ad2a
--- /dev/null
+++ b/src/common/config.cpp
@@ -0,0 +1,220 @@
+// Copyright (c) 2023 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 <common/args.h>
+
+#include <logging.h>
+#include <sync.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/fs.h>
+#include <util/settings.h>
+#include <util/string.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+fs::path GetConfigFile(const ArgsManager& args, const fs::path& configuration_file_path)
+{
+ return AbsPathForConfigVal(args, configuration_file_path, /*net_specific=*/false);
+}
+
+static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
+{
+ std::string str, prefix;
+ std::string::size_type pos;
+ int linenr = 1;
+ while (std::getline(stream, str)) {
+ bool used_hash = false;
+ if ((pos = str.find('#')) != std::string::npos) {
+ str = str.substr(0, pos);
+ used_hash = true;
+ }
+ const static std::string pattern = " \t\r\n";
+ str = TrimString(str, pattern);
+ if (!str.empty()) {
+ if (*str.begin() == '[' && *str.rbegin() == ']') {
+ const std::string section = str.substr(1, str.size() - 2);
+ sections.emplace_back(SectionInfo{section, filepath, linenr});
+ prefix = section + '.';
+ } else if (*str.begin() == '-') {
+ error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
+ return false;
+ } else if ((pos = str.find('=')) != std::string::npos) {
+ std::string name = prefix + TrimString(std::string_view{str}.substr(0, pos), pattern);
+ std::string_view value = TrimStringView(std::string_view{str}.substr(pos + 1), pattern);
+ if (used_hash && name.find("rpcpassword") != std::string::npos) {
+ error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
+ return false;
+ }
+ options.emplace_back(name, value);
+ if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
+ sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
+ }
+ } else {
+ error = strprintf("parse error on line %i: %s", linenr, str);
+ if (str.size() >= 2 && str.substr(0, 2) == "no") {
+ error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
+ }
+ return false;
+ }
+ }
+ ++linenr;
+ }
+ return true;
+}
+
+bool IsConfSupported(KeyInfo& key, std::string& error) {
+ if (key.name == "conf") {
+ error = "conf cannot be set in the configuration file; use includeconf= if you want to include additional config files";
+ return false;
+ }
+ if (key.name == "reindex") {
+ // reindex can be set in a config file but it is strongly discouraged as this will cause the node to reindex on
+ // every restart. Allow the config but throw a warning
+ LogPrintf("Warning: reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary\n");
+ return true;
+ }
+ return true;
+}
+
+bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
+{
+ LOCK(cs_args);
+ std::vector<std::pair<std::string, std::string>> options;
+ if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
+ return false;
+ }
+ for (const std::pair<std::string, std::string>& option : options) {
+ KeyInfo key = InterpretKey(option.first);
+ std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
+ if (!IsConfSupported(key, error)) return false;
+ if (flags) {
+ std::optional<util::SettingsValue> value = InterpretValue(key, &option.second, *flags, error);
+ if (!value) {
+ return false;
+ }
+ m_settings.ro_config[key.section][key.name].push_back(*value);
+ } else {
+ if (ignore_invalid_keys) {
+ LogPrintf("Ignoring unknown configuration value %s\n", option.first);
+ } else {
+ error = strprintf("Invalid configuration value %s", option.first);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
+{
+ {
+ LOCK(cs_args);
+ m_settings.ro_config.clear();
+ m_config_sections.clear();
+ }
+
+ const auto conf_path{GetConfigFilePath()};
+ std::ifstream stream{conf_path};
+
+ // not ok to have a config file specified that cannot be opened
+ if (IsArgSet("-conf") && !stream.good()) {
+ error = strprintf("specified config file \"%s\" could not be opened.", fs::PathToString(conf_path));
+ return false;
+ }
+ // ok to not have a config file
+ if (stream.good()) {
+ if (!ReadConfigStream(stream, fs::PathToString(conf_path), error, ignore_invalid_keys)) {
+ return false;
+ }
+ // `-includeconf` cannot be included in the command line arguments except
+ // as `-noincludeconf` (which indicates that no included conf file should be used).
+ bool use_conf_file{true};
+ {
+ LOCK(cs_args);
+ if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
+ // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
+ assert(util::SettingsSpan(*includes).last_negated());
+ use_conf_file = false;
+ }
+ }
+ if (use_conf_file) {
+ std::string chain_id = GetChainName();
+ std::vector<std::string> conf_file_names;
+
+ auto add_includes = [&](const std::string& network, size_t skip = 0) {
+ size_t num_values = 0;
+ LOCK(cs_args);
+ if (auto* section = util::FindKey(m_settings.ro_config, network)) {
+ if (auto* values = util::FindKey(*section, "includeconf")) {
+ for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
+ conf_file_names.push_back((*values)[i].get_str());
+ }
+ num_values = values->size();
+ }
+ }
+ return num_values;
+ };
+
+ // We haven't set m_network yet (that happens in SelectParams()), so manually check
+ // for network.includeconf args.
+ const size_t chain_includes = add_includes(chain_id);
+ const size_t default_includes = add_includes({});
+
+ for (const std::string& conf_file_name : conf_file_names) {
+ std::ifstream conf_file_stream{GetConfigFile(*this, fs::PathFromString(conf_file_name))};
+ if (conf_file_stream.good()) {
+ if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
+ return false;
+ }
+ LogPrintf("Included configuration file %s\n", conf_file_name);
+ } else {
+ error = "Failed to include configuration file " + conf_file_name;
+ return false;
+ }
+ }
+
+ // Warn about recursive -includeconf
+ conf_file_names.clear();
+ add_includes(chain_id, /* skip= */ chain_includes);
+ add_includes({}, /* skip= */ default_includes);
+ std::string chain_id_final = GetChainName();
+ if (chain_id_final != chain_id) {
+ // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
+ add_includes(chain_id_final);
+ }
+ for (const std::string& conf_file_name : conf_file_names) {
+ tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
+ }
+ }
+ }
+
+ // If datadir is changed in .conf file:
+ ClearPathCache();
+ if (!CheckDataDirOption(*this)) {
+ error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
+ return false;
+ }
+ return true;
+}
+
+fs::path AbsPathForConfigVal(const ArgsManager& args, const fs::path& path, bool net_specific)
+{
+ if (path.is_absolute()) {
+ return path;
+ }
+ return fsbridge::AbsPathJoin(net_specific ? args.GetDataDirNet() : args.GetDataDirBase(), path);
+}
diff --git a/src/common/init.cpp b/src/common/init.cpp
index e8fa7a14fd..6ffa44847a 100644
--- a/src/common/init.cpp
+++ b/src/common/init.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/args.h>
#include <common/init.h>
#include <tinyformat.h>
#include <util/fs.h>
-#include <util/system.h>
#include <util/translation.h>
#include <algorithm>
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 028c6ebae1..9160ec19e6 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -2,7 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util/system.h>
+#include <common/args.h>
+#include <logging.h>
#include <walletinitinterface.h>
class ArgsManager;
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 86166a5ca4..bf3fa6298d 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -4,13 +4,14 @@
#include <httprpc.h>
+#include <common/args.h>
#include <crypto/hmac_sha256.h>
#include <httpserver.h>
+#include <logging.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <walletinitinterface.h>
#include <algorithm>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 942caa042d..4f8a2b4d8d 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -9,7 +9,9 @@
#include <httpserver.h>
#include <chainparamsbase.h>
+#include <common/args.h>
#include <compat/compat.h>
+#include <logging.h>
#include <netbase.h>
#include <node/interface_ui.h>
#include <rpc/protocol.h> // For HTTP status codes
@@ -17,7 +19,6 @@
#include <sync.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
-#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
@@ -673,6 +674,9 @@ std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key
std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key)
{
evhttp_uri* uri_parsed{evhttp_uri_parse(uri)};
+ if (!uri_parsed) {
+ throw std::runtime_error("URI parsing failed, it likely contained RFC 3986 invalid characters");
+ }
const char* query{evhttp_uri_get_query(uri_parsed)};
std::optional<std::string> result;
diff --git a/src/i2p.cpp b/src/i2p.cpp
index 76fbb89536..c67b6ed185 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <compat/endian.h>
#include <crypto/sha256.h>
@@ -17,7 +18,6 @@
#include <util/sock.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <util/threadinterrupt.h>
#include <chrono>
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 8a311296c2..237c8e8be0 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/args.h>
#include <index/base.h>
#include <interfaces/chain.h>
#include <kernel/chain.h>
@@ -14,7 +15,6 @@
#include <shutdown.h>
#include <tinyformat.h>
#include <util/syscall_sandbox.h>
-#include <util/system.h>
#include <util/thread.h>
#include <util/translation.h>
#include <validation.h> // For g_chainman
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index 43c2215338..e6300ce3dd 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -4,12 +4,12 @@
#include <map>
+#include <common/args.h>
#include <dbwrapper.h>
#include <hash.h>
#include <index/blockfilterindex.h>
#include <node/blockstorage.h>
#include <util/fs_helpers.h>
-#include <util/system.h>
#include <validation.h>
using node::UndoReadFromDisk;
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 4d637e217a..5bc6ad3d31 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <coins.h>
+#include <common/args.h>
#include <crypto/muhash.h>
#include <index/coinstatsindex.h>
#include <kernel/coinstats.h>
@@ -12,7 +13,6 @@
#include <serialize.h>
#include <txdb.h>
#include <undo.h>
-#include <util/system.h>
#include <validation.h>
using kernel::CCoinsStats;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 9095e7afeb..49bddf2d4d 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -4,10 +4,10 @@
#include <index/txindex.h>
+#include <common/args.h>
#include <index/disktxpos.h>
#include <logging.h>
#include <node/blockstorage.h>
-#include <util/system.h>
#include <validation.h>
using node::OpenBlockFile;
diff --git a/src/init.cpp b/src/init.cpp
index 1122496539..525648b812 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -18,6 +18,7 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <deploymentstatus.h>
#include <hash.h>
diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp
index 100b4ef7ee..ddbdaa6cd0 100644
--- a/src/init/bitcoin-gui.cpp
+++ b/src/init/bitcoin-gui.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <interfaces/chain.h>
#include <interfaces/echo.h>
#include <interfaces/init.h>
@@ -10,7 +11,6 @@
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
-#include <util/system.h>
#include <memory>
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 1418e63777..b04596f986 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <interfaces/chain.h>
#include <interfaces/echo.h>
#include <interfaces/init.h>
@@ -10,7 +11,6 @@
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
-#include <util/system.h>
#include <memory>
diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp
index e27be0e598..dd5826d982 100644
--- a/src/init/bitcoin-qt.cpp
+++ b/src/init/bitcoin-qt.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <interfaces/chain.h>
#include <interfaces/echo.h>
#include <interfaces/init.h>
@@ -9,7 +10,6 @@
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
-#include <util/system.h>
#include <memory>
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 7ad1f64e64..210608370c 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <interfaces/chain.h>
#include <interfaces/echo.h>
#include <interfaces/init.h>
@@ -9,7 +10,6 @@
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
-#include <util/system.h>
#include <memory>
diff --git a/src/init/common.cpp b/src/init/common.cpp
index 33a5cec3f1..9a52a09cea 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -7,13 +7,13 @@
#endif
#include <clientversion.h>
+#include <common/args.h>
#include <logging.h>
#include <node/interface_ui.h>
#include <tinyformat.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index e0c4aff6f4..2827737ee1 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -103,8 +103,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021
- consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000003404ba0801921119f903495e");
- consensus.defaultAssumeValid = uint256S("0x00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd"); // 751565
+ consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000044a50fe819c39ad624021859");
+ consensus.defaultAssumeValid = uint256S("0x000000000000000000035c3f0d31e71a5ee24c5aaf3354689f65bd7b07dee632"); // 784000
/**
* The message start string is designed to be unlikely to occur in normal data.
@@ -117,8 +117,8 @@ public:
pchMessageStart[3] = 0xd9;
nDefaultPort = 8333;
nPruneAfterHeight = 100000;
- m_assumed_blockchain_size = 496;
- m_assumed_chain_state_size = 6;
+ m_assumed_blockchain_size = 540;
+ m_assumed_chain_state_size = 7;
genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
@@ -178,10 +178,10 @@ public:
};
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd
- .nTime = 1661697692,
- .nTxCount = 760120522,
- .dTxRate = 2.925802860942233,
+ // Data from RPC: getchaintxstats 4096 000000000000000000035c3f0d31e71a5ee24c5aaf3354689f65bd7b07dee632
+ .nTime = 1680665245,
+ .nTxCount = 820876044,
+ .dTxRate = 3.672283614033389,
};
}
};
@@ -223,8 +223,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
- consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000076f6e7cbd0beade5d20");
- consensus.defaultAssumeValid = uint256S("0x0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060"); // 2344474
+ consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000977edb0244170858d07");
+ consensus.defaultAssumeValid = uint256S("0x0000000000000021bc50a89cde4870d4a81ffe0153b3c8de77b435a2fd3f6761"); // 2429000
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
@@ -233,7 +233,7 @@ public:
nDefaultPort = 18333;
nPruneAfterHeight = 1000;
m_assumed_blockchain_size = 42;
- m_assumed_chain_state_size = 2;
+ m_assumed_chain_state_size = 3;
genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
@@ -274,10 +274,10 @@ public:
};
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060
- .nTime = 1661705221,
- .nTxCount = 63531852,
- .dTxRate = 0.1079119341520164,
+ // Data from RPC: getchaintxstats 4096 0000000000000021bc50a89cde4870d4a81ffe0153b3c8de77b435a2fd3f6761
+ .nTime = 1681542696,
+ .nTxCount = 65345929,
+ .dTxRate = 0.09855282814711661,
};
}
};
@@ -300,15 +300,15 @@ public:
vSeeds.emplace_back("178.128.221.177");
vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333");
- consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001291fc22898");
- consensus.defaultAssumeValid = uint256S("0x000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09"); // 105495
+ consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001899d8142b0");
+ consensus.defaultAssumeValid = uint256S("0x0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec"); // 138000
m_assumed_blockchain_size = 1;
m_assumed_chain_state_size = 0;
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09
- .nTime = 1661702566,
- .nTxCount = 1903567,
- .dTxRate = 0.02336701143027275,
+ // Data from RPC: getchaintxstats 4096 0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec
+ .nTime = 1681127428,
+ .nTxCount = 2226359,
+ .dTxRate = 0.006424463050600656,
};
} else {
bin = *options.challenge;
diff --git a/src/memusage.h b/src/memusage.h
index 9755be0ff5..bb39066a7d 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -7,6 +7,7 @@
#include <indirectmap.h>
#include <prevector.h>
+#include <support/allocators/pool.h>
#include <cassert>
#include <cstdlib>
@@ -166,6 +167,25 @@ static inline size_t DynamicUsage(const std::unordered_map<X, Y, Z>& m)
return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
}
+template <class Key, class T, class Hash, class Pred, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+static inline size_t DynamicUsage(const std::unordered_map<Key,
+ T,
+ Hash,
+ Pred,
+ PoolAllocator<std::pair<const Key, T>,
+ MAX_BLOCK_SIZE_BYTES,
+ ALIGN_BYTES>>& m)
+{
+ auto* pool_resource = m.get_allocator().resource();
+
+ // The allocated chunks are stored in a std::list. Size per node should
+ // therefore be 3 pointers: next, previous, and a pointer to the chunk.
+ size_t estimated_list_node_size = MallocUsage(sizeof(void*) * 3);
+ size_t usage_resource = estimated_list_node_size * pool_resource->NumAllocatedChunks();
+ size_t usage_chunks = MallocUsage(pool_resource->ChunkSizeBytes()) * pool_resource->NumAllocatedChunks();
+ return usage_resource + usage_chunks + MallocUsage(sizeof(void*) * m.bucket_count());
}
+} // namespace memusage
+
#endif // BITCOIN_MEMUSAGE_H
diff --git a/src/net.cpp b/src/net.cpp
index 903fedb2fb..337cf60680 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -13,6 +13,7 @@
#include <addrman.h>
#include <banman.h>
#include <clientversion.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
@@ -30,7 +31,6 @@
#include <util/sock.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
-#include <util/system.h>
#include <util/thread.h>
#include <util/threadinterrupt.h>
#include <util/trace.h>
@@ -2614,6 +2614,11 @@ size_t CConnman::GetNodeCount(ConnectionDirection flags) const
return nNum;
}
+uint32_t CConnman::GetMappedAS(const CNetAddr& addr) const
+{
+ return m_netgroupman.GetMappedAS(addr);
+}
+
void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
{
vstats.clear();
@@ -2622,7 +2627,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
for (CNode* pnode : m_nodes) {
vstats.emplace_back();
pnode->CopyStats(vstats.back());
- vstats.back().m_mapped_as = m_netgroupman.GetMappedAS(pnode->addr);
+ vstats.back().m_mapped_as = GetMappedAS(pnode->addr);
}
}
diff --git a/src/net.h b/src/net.h
index 9b939aea5c..908b16f35e 100644
--- a/src/net.h
+++ b/src/net.h
@@ -851,6 +851,7 @@ public:
bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
size_t GetNodeCount(ConnectionDirection) const;
+ uint32_t GetMappedAS(const CNetAddr& addr) const;
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
bool DisconnectNode(const std::string& node);
bool DisconnectNode(const CSubNet& subnet);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 68bd91297c..c50aa2e4f9 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -10,6 +10,7 @@
#include <blockencodings.h>
#include <blockfilter.h>
#include <chainparams.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
@@ -39,7 +40,6 @@
#include <txrequest.h>
#include <util/check.h> // For NDEBUG compile time check
#include <util/strencodings.h>
-#include <util/system.h>
#include <util/trace.h>
#include <validation.h>
@@ -3362,10 +3362,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (fLogIPs)
remoteAddr = ", peeraddr=" + pfrom.addr.ToStringAddrPort();
- LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, txrelay=%d, peer=%d%s\n",
+ const auto mapped_as{m_connman.GetMappedAS(pfrom.addr)};
+ LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, txrelay=%d, peer=%d%s%s\n",
cleanSubVer, pfrom.nVersion,
peer->m_starting_height, addrMe.ToStringAddrPort(), fRelay, pfrom.GetId(),
- remoteAddr);
+ remoteAddr, (mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""));
int64_t nTimeOffset = nTime - GetTime();
pfrom.nTimeOffset = nTimeOffset;
@@ -3405,9 +3406,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (!pfrom.IsInboundConn()) {
- LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
+ const auto mapped_as{m_connman.GetMappedAS(pfrom.addr)};
+ LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s%s (%s)\n",
pfrom.nVersion.load(), peer->m_starting_height,
pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToStringAddrPort()) : ""),
+ (mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""),
pfrom.ConnectionTypeAsString());
}
diff --git a/src/node/blockmanager_args.cpp b/src/node/blockmanager_args.cpp
index 5fb5c8beed..06a1934947 100644
--- a/src/node/blockmanager_args.cpp
+++ b/src/node/blockmanager_args.cpp
@@ -4,7 +4,7 @@
#include <node/blockmanager_args.h>
-#include <util/system.h>
+#include <common/args.h>
#include <validation.h>
namespace node {
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index af84e6d7e7..38859f1b0d 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -6,11 +6,12 @@
#include <chain.h>
#include <clientversion.h>
+#include <common/args.h>
#include <consensus/validation.h>
#include <flatfile.h>
#include <hash.h>
-#include <logging.h>
#include <kernel/chainparams.h>
+#include <logging.h>
#include <pow.h>
#include <reverse_iterator.h>
#include <shutdown.h>
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index 7622a03e19..7403f7ddea 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -4,9 +4,9 @@
#include <node/caches.h>
+#include <common/args.h>
#include <index/txindex.h>
#include <txdb.h>
-#include <util/system.h>
namespace node {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
index 9801e6e959..b97344c9aa 100644
--- a/src/node/chainstatemanager_args.cpp
+++ b/src/node/chainstatemanager_args.cpp
@@ -5,13 +5,13 @@
#include <node/chainstatemanager_args.h>
#include <arith_uint256.h>
+#include <common/args.h>
#include <kernel/chainstatemanager_opts.h>
#include <node/coins_view_args.h>
#include <node/database_args.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
diff --git a/src/node/coins_view_args.cpp b/src/node/coins_view_args.cpp
index 67c9b8dbac..5d55143e83 100644
--- a/src/node/coins_view_args.cpp
+++ b/src/node/coins_view_args.cpp
@@ -4,8 +4,8 @@
#include <node/coins_view_args.h>
+#include <common/args.h>
#include <txdb.h>
-#include <util/system.h>
namespace node {
void ReadCoinsViewArgs(const ArgsManager& args, CoinsViewOptions& options)
diff --git a/src/node/database_args.cpp b/src/node/database_args.cpp
index 2c53b4b47e..aba3c38ff3 100644
--- a/src/node/database_args.cpp
+++ b/src/node/database_args.cpp
@@ -4,8 +4,8 @@
#include <node/database_args.h>
+#include <common/args.h>
#include <dbwrapper.h>
-#include <util/system.h>
namespace node {
void ReadDatabaseArgs(const ArgsManager& args, DBOptions& options)
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 50ed4c2ab8..5fcace833f 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -7,6 +7,7 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
+#include <common/args.h>
#include <deploymentstatus.h>
#include <external_signer.h>
#include <index/blockfilterindex.h>
@@ -43,7 +44,6 @@
#include <uint256.h>
#include <univalue.h>
#include <util/check.h>
-#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp
index a0a2e43107..f193469506 100644
--- a/src/node/mempool_args.cpp
+++ b/src/node/mempool_args.cpp
@@ -7,6 +7,7 @@
#include <kernel/mempool_limits.h>
#include <kernel/mempool_options.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <kernel/chainparams.h>
#include <logging.h>
@@ -16,7 +17,6 @@
#include <tinyformat.h>
#include <util/error.h>
#include <util/moneystr.h>
-#include <util/system.h>
#include <util/translation.h>
#include <chrono>
diff --git a/src/node/mempool_persist_args.cpp b/src/node/mempool_persist_args.cpp
index 78e3472644..97ecdd651b 100644
--- a/src/node/mempool_persist_args.cpp
+++ b/src/node/mempool_persist_args.cpp
@@ -4,8 +4,8 @@
#include <node/mempool_persist_args.h>
+#include <common/args.h>
#include <util/fs.h>
-#include <util/system.h>
#include <validation.h>
namespace node {
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index c7bc9a9a3d..aa1a9a155c 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -8,6 +8,7 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
@@ -20,7 +21,6 @@
#include <primitives/transaction.h>
#include <timedata.h>
#include <util/moneystr.h>
-#include <util/system.h>
#include <validation.h>
#include <algorithm>
diff --git a/src/node/txreconciliation.cpp b/src/node/txreconciliation.cpp
index ed04a78cec..9938759074 100644
--- a/src/node/txreconciliation.cpp
+++ b/src/node/txreconciliation.cpp
@@ -4,6 +4,7 @@
#include <node/txreconciliation.h>
+#include <logging.h>
#include <util/check.h>
#include <util/system.h>
diff --git a/src/node/utxo_snapshot.cpp b/src/node/utxo_snapshot.cpp
index 591c1dad6e..3dae46fb84 100644
--- a/src/node/utxo_snapshot.cpp
+++ b/src/node/utxo_snapshot.cpp
@@ -4,6 +4,7 @@
#include <node/utxo_snapshot.h>
+#include <common/args.h>
#include <logging.h>
#include <streams.h>
#include <sync.h>
@@ -11,7 +12,6 @@
#include <txdb.h>
#include <uint256.h>
#include <util/fs.h>
-#include <util/system.h>
#include <validation.h>
#include <cassert>
diff --git a/src/node/validation_cache_args.cpp b/src/node/validation_cache_args.cpp
index 5ea0a8ca0a..ddf24f798d 100644
--- a/src/node/validation_cache_args.cpp
+++ b/src/node/validation_cache_args.cpp
@@ -6,7 +6,7 @@
#include <kernel/validation_cache_sizes.h>
-#include <util/system.h>
+#include <common/args.h>
#include <algorithm>
#include <cstddef>
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 270212dca5..9a3870d8dc 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -32,8 +32,6 @@ std::optional<OutputType> ParseOutputType(const std::string& type)
return OutputType::BECH32;
} else if (type == OUTPUT_TYPE_STRING_BECH32M) {
return OutputType::BECH32M;
- } else if (type == OUTPUT_TYPE_STRING_UNKNOWN) {
- return OutputType::UNKNOWN;
}
return std::nullopt;
}
diff --git a/src/policy/fees_args.cpp b/src/policy/fees_args.cpp
index 1aeb2ab983..988b6d443a 100644
--- a/src/policy/fees_args.cpp
+++ b/src/policy/fees_args.cpp
@@ -4,7 +4,7 @@
#include <policy/fees_args.h>
-#include <util/system.h>
+#include <common/args.h>
namespace {
const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
diff --git a/src/protocol.cpp b/src/protocol.cpp
index aa59bae6ff..5725813b7e 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -7,6 +7,8 @@
#include <util/system.h>
+#include <atomic>
+
static std::atomic<bool> g_initial_block_download_completed(false);
namespace NetMsgType {
diff --git a/src/psbt.cpp b/src/psbt.cpp
index fe45f2318c..009ed966ed 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -442,6 +442,38 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
return sig_complete;
}
+void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type)
+{
+ // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
+ if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
+ // Figure out if any non_witness_utxos should be dropped
+ std::vector<unsigned int> to_drop;
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const auto& input = psbtx.inputs.at(i);
+ int wit_ver;
+ std::vector<unsigned char> wit_prog;
+ if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
+ // There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
+ to_drop.clear();
+ break;
+ }
+ if (wit_ver == 0) {
+ // Segwit v0, so we cannot drop any non_witness_utxos
+ to_drop.clear();
+ break;
+ }
+ if (input.non_witness_utxo) {
+ to_drop.push_back(i);
+ }
+ }
+
+ // Drop the non_witness_utxos that we can drop
+ for (unsigned int i : to_drop) {
+ psbtx.inputs.at(i).non_witness_utxo = nullptr;
+ }
+ }
+}
+
bool FinalizePSBT(PartiallySignedTransaction& psbtx)
{
// Finalize input signatures -- in case we have partial signatures that add up to a complete
diff --git a/src/psbt.h b/src/psbt.h
index c497584f36..9464b10268 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -1231,6 +1231,9 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
**/
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
+/** Reduces the size of the PSBT by dropping unnecessary `non_witness_utxos` (i.e. complete previous transactions) from a psbt when all inputs are segwit v1. */
+void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type);
+
/** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index f604039427..2f00009596 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -9,6 +9,7 @@
#include <qt/bitcoin.h>
#include <chainparams.h>
+#include <common/args.h>
#include <common/init.h>
#include <init.h>
#include <interfaces/handler.h>
@@ -494,7 +495,7 @@ static void SetupUIArgs(ArgsManager& argsman)
int GuiMain(int argc, char* argv[])
{
#ifdef WIN32
- util::WinCmdLineArgs winArgs;
+ common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 8411ec4696..837e5f4fed 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -11,6 +11,7 @@
#include <qt/peertablesortproxy.h>
#include <clientversion.h>
+#include <common/args.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <net.h>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index e98d953fd2..dc7daed4bc 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -12,8 +12,10 @@
#include <base58.h>
#include <chainparams.h>
+#include <common/args.h>
#include <interfaces/node.h>
#include <key_io.h>
+#include <logging.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <protocol.h>
@@ -22,7 +24,6 @@
#include <util/exception.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
-#include <util/system.h>
#include <util/time.h>
#ifdef WIN32
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 6423e457aa..a54ba19354 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -15,9 +15,9 @@
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <common/args.h>
#include <interfaces/node.h>
#include <util/fs_helpers.h>
-#include <util/system.h>
#include <validation.h>
#include <QFileDialog>
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index bee8fafddc..87a7e703b8 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -12,14 +12,14 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
+#include <common/args.h>
#include <interfaces/node.h>
#include <mapport.h>
#include <net.h>
#include <netbase.h>
-#include <txdb.h> // for -dbcache defaults
+#include <txdb.h> // for -dbcache defaults
#include <util/string.h>
-#include <util/system.h>
-#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
+#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
#include <QDebug>
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 3f9d1b040b..592e591edb 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -13,11 +13,11 @@
#include <qt/optionsmodel.h>
#include <chainparams.h>
+#include <common/args.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <node/interface_ui.h>
#include <policy/policy.h>
-#include <util/system.h>
#include <wallet/wallet.h>
#include <cstdlib>
diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp
index dc7c8928c5..0838e21678 100644
--- a/src/qt/test/optiontests.cpp
+++ b/src/qt/test/optiontests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <init.h>
#include <qt/bitcoin.h>
#include <qt/guiutil.h>
#include <qt/test/optiontests.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <QSettings>
#include <QTest>
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index eb2ab12a66..f43b993628 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -13,8 +13,8 @@
#include <qt/guiutil.h>
#include <clientversion.h>
+#include <common/args.h>
#include <init.h>
-#include <util/system.h>
#include <util/strencodings.h>
#include <cstdio>
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index fdd96c664a..ee3327530c 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -18,12 +18,12 @@
#include <qt/sendcoinsdialog.h>
#include <qt/transactiontablemodel.h>
+#include <common/args.h> // for GetBoolArg
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <node/interface_ui.h>
#include <psbt.h>
-#include <util/system.h> // for GetBoolArg
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/wallet.h> // for CRecipient
diff --git a/src/rest.cpp b/src/rest.cpp
index fa119ddfb7..e46406f1ad 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -200,7 +200,11 @@ static bool rest_headers(const std::any& context,
} else if (path.size() == 1) {
// new path with query parameter: /rest/headers/<hash>?count=<count>
hashStr = path[0];
- raw_count = req->GetQueryParameter("count").value_or("5");
+ try {
+ raw_count = req->GetQueryParameter("count").value_or("5");
+ } catch (const std::runtime_error& e) {
+ return RESTERR(req, HTTP_BAD_REQUEST, e.what());
+ }
} else {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
}
@@ -371,7 +375,11 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
} else if (uri_parts.size() == 2) {
// new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
raw_blockhash = uri_parts[1];
- raw_count = req->GetQueryParameter("count").value_or("5");
+ try {
+ raw_count = req->GetQueryParameter("count").value_or("5");
+ } catch (const std::runtime_error& e) {
+ return RESTERR(req, HTTP_BAD_REQUEST, e.what());
+ }
} else {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
}
@@ -652,11 +660,21 @@ static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::s
case RESTResponseFormat::JSON: {
std::string str_json;
if (param == "contents") {
- const std::string raw_verbose{req->GetQueryParameter("verbose").value_or("true")};
+ std::string raw_verbose;
+ try {
+ raw_verbose = req->GetQueryParameter("verbose").value_or("true");
+ } catch (const std::runtime_error& e) {
+ return RESTERR(req, HTTP_BAD_REQUEST, e.what());
+ }
if (raw_verbose != "true" && raw_verbose != "false") {
return RESTERR(req, HTTP_BAD_REQUEST, "The \"verbose\" query parameter must be either \"true\" or \"false\".");
}
- const std::string raw_mempool_sequence{req->GetQueryParameter("mempool_sequence").value_or("false")};
+ std::string raw_mempool_sequence;
+ try {
+ raw_mempool_sequence = req->GetQueryParameter("mempool_sequence").value_or("false");
+ } catch (const std::runtime_error& e) {
+ return RESTERR(req, HTTP_BAD_REQUEST, e.what());
+ }
if (raw_mempool_sequence != "true" && raw_mempool_sequence != "false") {
return RESTERR(req, HTTP_BAD_REQUEST, "The \"mempool_sequence\" query parameter must be either \"true\" or \"false\".");
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index fb22321d90..49921794bf 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -9,6 +9,7 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/params.h>
#include <consensus/validation.h>
@@ -40,7 +41,6 @@
#include <util/check.h>
#include <util/fs.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 4459dd71aa..f3c19003ff 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -3,9 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <rpc/client.h>
#include <tinyformat.h>
-#include <util/system.h>
#include <set>
#include <stdint.h>
diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp
index f5a6913572..1e139c9990 100644
--- a/src/rpc/external_signer.cpp
+++ b/src/rpc/external_signer.cpp
@@ -3,11 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparamsbase.h>
+#include <common/args.h>
#include <external_signer.h>
+#include <rpc/protocol.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <util/strencodings.h>
-#include <rpc/protocol.h>
+#include <util/system.h>
#include <string>
#include <vector>
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 21d49fda9d..4a918cbd42 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -172,6 +172,91 @@ static std::vector<RPCArg> CreateTxDoc()
};
}
+// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors
+PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider)
+{
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, psbt_string, error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ if (g_txindex) g_txindex->BlockUntilSyncedToCurrentChain();
+ const NodeContext& node = EnsureAnyNodeContext(context);
+
+ // If we can't find the corresponding full transaction for all of our inputs,
+ // this will be used to find just the utxos for the segwit inputs for which
+ // the full transaction isn't found
+ std::map<COutPoint, Coin> coins;
+
+ // Fetch previous transactions:
+ // First, look in the txindex and the mempool
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& psbt_input = psbtx.inputs.at(i);
+ const CTxIn& tx_in = psbtx.tx->vin.at(i);
+
+ // The `non_witness_utxo` is the whole previous transaction
+ if (psbt_input.non_witness_utxo) continue;
+
+ CTransactionRef tx;
+
+ // Look in the txindex
+ if (g_txindex) {
+ uint256 block_hash;
+ g_txindex->FindTx(tx_in.prevout.hash, block_hash, tx);
+ }
+ // If we still don't have it look in the mempool
+ if (!tx) {
+ tx = node.mempool->get(tx_in.prevout.hash);
+ }
+ if (tx) {
+ psbt_input.non_witness_utxo = tx;
+ } else {
+ coins[tx_in.prevout]; // Create empty map entry keyed by prevout
+ }
+ }
+
+ // If we still haven't found all of the inputs, look for the missing ones in the utxo set
+ if (!coins.empty()) {
+ FindCoins(node, coins);
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ // If there are still missing utxos, add them if they were found in the utxo set
+ if (!input.non_witness_utxo) {
+ const CTxIn& tx_in = psbtx.tx->vin.at(i);
+ const Coin& coin = coins.at(tx_in.prevout);
+ if (!coin.out.IsNull() && IsSegWitOutput(provider, coin.out.scriptPubKey)) {
+ input.witness_utxo = coin.out;
+ }
+ }
+ }
+ }
+
+ const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx);
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ if (PSBTInputSigned(psbtx.inputs.at(i))) {
+ continue;
+ }
+
+ // Update script/keypath information using descriptor data.
+ // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
+ // we don't actually care about those here, in fact.
+ SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
+ }
+
+ // Update script/keypath information using descriptor data.
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ UpdatePSBTOutput(provider, psbtx, i);
+ }
+
+ RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1);
+
+ return psbtx;
+}
+
static RPCHelpMan getrawtransaction()
{
return RPCHelpMan{
@@ -1580,7 +1665,7 @@ static RPCHelpMan converttopsbt()
static RPCHelpMan utxoupdatepsbt()
{
return RPCHelpMan{"utxoupdatepsbt",
- "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
+ "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set, txindex, or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
{"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of either strings or objects", {
@@ -1599,13 +1684,6 @@ static RPCHelpMan utxoupdatepsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- // Unserialize the transactions
- PartiallySignedTransaction psbtx;
- std::string error;
- if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
- }
-
// Parse descriptors, if any.
FlatSigningProvider provider;
if (!request.params[1].isNull()) {
@@ -1614,53 +1692,12 @@ static RPCHelpMan utxoupdatepsbt()
EvalDescriptorStringOrObject(descs[i], provider);
}
}
- // We don't actually need private keys further on; hide them as a precaution.
- HidingSigningProvider public_provider(&provider, /*hide_secret=*/true, /*hide_origin=*/false);
-
- // Fetch previous transactions (inputs):
- CCoinsView viewDummy;
- CCoinsViewCache view(&viewDummy);
- {
- NodeContext& node = EnsureAnyNodeContext(request.context);
- const CTxMemPool& mempool = EnsureMemPool(node);
- ChainstateManager& chainman = EnsureChainman(node);
- LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
- CCoinsViewMemPool viewMempool(&viewChain, mempool);
- view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
-
- for (const CTxIn& txin : psbtx.tx->vin) {
- view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
- }
-
- view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
- }
-
- // Fill the inputs
- const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs.at(i);
- if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
- continue;
- }
-
- const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
-
- if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
- input.witness_utxo = coin.out;
- }
-
- // Update script/keypath information using descriptor data.
- // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
- // we don't actually care about those here, in fact.
- SignPSBTInput(public_provider, psbtx, i, &txdata, /*sighash=*/1);
- }
-
- // Update script/keypath information using descriptor data.
- for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- UpdatePSBTOutput(public_provider, psbtx, i);
- }
+ // We don't actually need private keys further on; hide them as a precaution.
+ const PartiallySignedTransaction& psbtx = ProcessPSBT(
+ request.params[0].get_str(),
+ request.context,
+ HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index 4c5ccbbdad..ad91ed0f23 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -7,11 +7,12 @@
#include <util/fs.h>
+#include <common/args.h>
+#include <logging.h>
#include <random.h>
#include <rpc/protocol.h>
#include <util/fs_helpers.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <fstream>
#include <stdexcept>
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 392238a1fd..354f949002 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -5,6 +5,8 @@
#include <rpc/server.h>
+#include <common/args.h>
+#include <logging.h>
#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp
index 7a708ec813..13d007b496 100644
--- a/src/rpc/server_util.cpp
+++ b/src/rpc/server_util.cpp
@@ -4,6 +4,7 @@
#include <rpc/server_util.h>
+#include <common/args.h>
#include <net_processing.h>
#include <node/context.h>
#include <policy/fees.h>
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index f95ac4cb4b..1f3f37d0a0 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <clientversion.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <key_io.h>
#include <outputtype.h>
@@ -13,7 +14,6 @@
#include <util/check.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/translation.h>
#include <tuple>
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index b4ad1e3f10..0951504ee0 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -12,10 +12,10 @@
#include <script/standard.h>
#include <uint256.h>
+#include <common/args.h>
#include <span.h>
#include <util/bip32.h>
#include <util/spanparsing.h>
-#include <util/system.h>
#include <util/strencodings.h>
#include <util/vector.h>
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index fef3601887..e52e8cd309 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -5,6 +5,7 @@
#include <script/sigcache.h>
+#include <logging.h>
#include <pubkey.h>
#include <random.h>
#include <uint256.h>
diff --git a/src/signet.cpp b/src/signet.cpp
index 747bd1b0a8..b76b1e342f 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -13,15 +13,16 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
+#include <logging.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <span.h>
#include <script/interpreter.h>
#include <script/standard.h>
+#include <span.h>
#include <streams.h>
+#include <uint256.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <uint256.h>
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
diff --git a/src/support/allocators/pool.h b/src/support/allocators/pool.h
new file mode 100644
index 0000000000..c8e70ebacf
--- /dev/null
+++ b/src/support/allocators/pool.h
@@ -0,0 +1,349 @@
+// Copyright (c) 2022 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_SUPPORT_ALLOCATORS_POOL_H
+#define BITCOIN_SUPPORT_ALLOCATORS_POOL_H
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <list>
+#include <memory>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+/**
+ * A memory resource similar to std::pmr::unsynchronized_pool_resource, but
+ * optimized for node-based containers. It has the following properties:
+ *
+ * * Owns the allocated memory and frees it on destruction, even when deallocate
+ * has not been called on the allocated blocks.
+ *
+ * * Consists of a number of pools, each one for a different block size.
+ * Each pool holds blocks of uniform size in a freelist.
+ *
+ * * Exhausting memory in a freelist causes a new allocation of a fixed size chunk.
+ * This chunk is used to carve out blocks.
+ *
+ * * Block sizes or alignments that can not be served by the pools are allocated
+ * and deallocated by operator new().
+ *
+ * PoolResource is not thread-safe. It is intended to be used by PoolAllocator.
+ *
+ * @tparam MAX_BLOCK_SIZE_BYTES Maximum size to allocate with the pool. If larger
+ * sizes are requested, allocation falls back to new().
+ *
+ * @tparam ALIGN_BYTES Required alignment for the allocations.
+ *
+ * An example: If you create a PoolResource<128, 8>(262144) and perform a bunch of
+ * allocations and deallocate 2 blocks with size 8 bytes, and 3 blocks with size 16,
+ * the members will look like this:
+ *
+ * m_free_lists m_allocated_chunks
+ * ┌───┐ ┌───┐ ┌────────────-------──────┐
+ * │ │ blocks │ ├─►│ 262144 B │
+ * │ │ ┌─────┐ ┌─────┐ └─┬─┘ └────────────-------──────┘
+ * │ 1 ├─►│ 8 B ├─►│ 8 B │ │
+ * │ │ └─────┘ └─────┘ :
+ * │ │ │
+ * │ │ ┌─────┐ ┌─────┐ ┌─────┐ ▼
+ * │ 2 ├─►│16 B ├─►│16 B ├─►│16 B │ ┌───┐ ┌─────────────────────────┐
+ * │ │ └─────┘ └─────┘ └─────┘ │ ├─►│ ▲ │ ▲
+ * │ │ └───┘ └──────────┬──────────────┘ │
+ * │ . │ │ m_available_memory_end
+ * │ . │ m_available_memory_it
+ * │ . │
+ * │ │
+ * │ │
+ * │16 │
+ * └───┘
+ *
+ * Here m_free_lists[1] holds the 2 blocks of size 8 bytes, and m_free_lists[2]
+ * holds the 3 blocks of size 16. The blocks came from the data stored in the
+ * m_allocated_chunks list. Each chunk has bytes 262144. The last chunk has still
+ * some memory available for the blocks, and when m_available_memory_it is at the
+ * end, a new chunk will be allocated and added to the list.
+ */
+template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+class PoolResource final
+{
+ static_assert(ALIGN_BYTES > 0, "ALIGN_BYTES must be nonzero");
+ static_assert((ALIGN_BYTES & (ALIGN_BYTES - 1)) == 0, "ALIGN_BYTES must be a power of two");
+
+ /**
+ * In-place linked list of the allocations, used for the freelist.
+ */
+ struct ListNode {
+ ListNode* m_next;
+
+ explicit ListNode(ListNode* next) : m_next(next) {}
+ };
+ static_assert(std::is_trivially_destructible_v<ListNode>, "Make sure we don't need to manually call a destructor");
+
+ /**
+ * Internal alignment value. The larger of the requested ALIGN_BYTES and alignof(FreeList).
+ */
+ static constexpr std::size_t ELEM_ALIGN_BYTES = std::max(alignof(ListNode), ALIGN_BYTES);
+ static_assert((ELEM_ALIGN_BYTES & (ELEM_ALIGN_BYTES - 1)) == 0, "ELEM_ALIGN_BYTES must be a power of two");
+ static_assert(sizeof(ListNode) <= ELEM_ALIGN_BYTES, "Units of size ELEM_SIZE_ALIGN need to be able to store a ListNode");
+ static_assert((MAX_BLOCK_SIZE_BYTES & (ELEM_ALIGN_BYTES - 1)) == 0, "MAX_BLOCK_SIZE_BYTES needs to be a multiple of the alignment.");
+
+ /**
+ * Size in bytes to allocate per chunk
+ */
+ const size_t m_chunk_size_bytes;
+
+ /**
+ * Contains all allocated pools of memory, used to free the data in the destructor.
+ */
+ std::list<std::byte*> m_allocated_chunks{};
+
+ /**
+ * Single linked lists of all data that came from deallocating.
+ * m_free_lists[n] will serve blocks of size n*ELEM_ALIGN_BYTES.
+ */
+ std::array<ListNode*, MAX_BLOCK_SIZE_BYTES / ELEM_ALIGN_BYTES + 1> m_free_lists{};
+
+ /**
+ * Points to the beginning of available memory for carving out allocations.
+ */
+ std::byte* m_available_memory_it = nullptr;
+
+ /**
+ * Points to the end of available memory for carving out allocations.
+ *
+ * That member variable is redundant, and is always equal to `m_allocated_chunks.back() + m_chunk_size_bytes`
+ * whenever it is accessed, but `m_available_memory_end` caches this for clarity and efficiency.
+ */
+ std::byte* m_available_memory_end = nullptr;
+
+ /**
+ * How many multiple of ELEM_ALIGN_BYTES are necessary to fit bytes. We use that result directly as an index
+ * into m_free_lists. Round up for the special case when bytes==0.
+ */
+ [[nodiscard]] static constexpr std::size_t NumElemAlignBytes(std::size_t bytes)
+ {
+ return (bytes + ELEM_ALIGN_BYTES - 1) / ELEM_ALIGN_BYTES + (bytes == 0);
+ }
+
+ /**
+ * True when it is possible to make use of the freelist
+ */
+ [[nodiscard]] static constexpr bool IsFreeListUsable(std::size_t bytes, std::size_t alignment)
+ {
+ return alignment <= ELEM_ALIGN_BYTES && bytes <= MAX_BLOCK_SIZE_BYTES;
+ }
+
+ /**
+ * Replaces node with placement constructed ListNode that points to the previous node
+ */
+ void PlacementAddToList(void* p, ListNode*& node)
+ {
+ node = new (p) ListNode{node};
+ }
+
+ /**
+ * Allocate one full memory chunk which will be used to carve out allocations.
+ * Also puts any leftover bytes into the freelist.
+ *
+ * Precondition: leftover bytes are either 0 or few enough to fit into a place in the freelist
+ */
+ void AllocateChunk()
+ {
+ // if there is still any available memory left, put it into the freelist.
+ size_t remaining_available_bytes = std::distance(m_available_memory_it, m_available_memory_end);
+ if (0 != remaining_available_bytes) {
+ PlacementAddToList(m_available_memory_it, m_free_lists[remaining_available_bytes / ELEM_ALIGN_BYTES]);
+ }
+
+ void* storage = ::operator new (m_chunk_size_bytes, std::align_val_t{ELEM_ALIGN_BYTES});
+ m_available_memory_it = new (storage) std::byte[m_chunk_size_bytes];
+ m_available_memory_end = m_available_memory_it + m_chunk_size_bytes;
+ m_allocated_chunks.emplace_back(m_available_memory_it);
+ }
+
+ /**
+ * Access to internals for testing purpose only
+ */
+ friend class PoolResourceTester;
+
+public:
+ /**
+ * Construct a new PoolResource object which allocates the first chunk.
+ * chunk_size_bytes will be rounded up to next multiple of ELEM_ALIGN_BYTES.
+ */
+ explicit PoolResource(std::size_t chunk_size_bytes)
+ : m_chunk_size_bytes(NumElemAlignBytes(chunk_size_bytes) * ELEM_ALIGN_BYTES)
+ {
+ assert(m_chunk_size_bytes >= MAX_BLOCK_SIZE_BYTES);
+ AllocateChunk();
+ }
+
+ /**
+ * Construct a new Pool Resource object, defaults to 2^18=262144 chunk size.
+ */
+ PoolResource() : PoolResource(262144) {}
+
+ /**
+ * Disable copy & move semantics, these are not supported for the resource.
+ */
+ PoolResource(const PoolResource&) = delete;
+ PoolResource& operator=(const PoolResource&) = delete;
+ PoolResource(PoolResource&&) = delete;
+ PoolResource& operator=(PoolResource&&) = delete;
+
+ /**
+ * Deallocates all memory allocated associated with the memory resource.
+ */
+ ~PoolResource()
+ {
+ for (std::byte* chunk : m_allocated_chunks) {
+ std::destroy(chunk, chunk + m_chunk_size_bytes);
+ ::operator delete ((void*)chunk, std::align_val_t{ELEM_ALIGN_BYTES});
+ }
+ }
+
+ /**
+ * Allocates a block of bytes. If possible the freelist is used, otherwise allocation
+ * is forwarded to ::operator new().
+ */
+ void* Allocate(std::size_t bytes, std::size_t alignment)
+ {
+ if (IsFreeListUsable(bytes, alignment)) {
+ const std::size_t num_alignments = NumElemAlignBytes(bytes);
+ if (nullptr != m_free_lists[num_alignments]) {
+ // we've already got data in the pool's freelist, unlink one element and return the pointer
+ // to the unlinked memory. Since FreeList is trivially destructible we can just treat it as
+ // uninitialized memory.
+ return std::exchange(m_free_lists[num_alignments], m_free_lists[num_alignments]->m_next);
+ }
+
+ // freelist is empty: get one allocation from allocated chunk memory.
+ const std::ptrdiff_t round_bytes = static_cast<std::ptrdiff_t>(num_alignments * ELEM_ALIGN_BYTES);
+ if (round_bytes > m_available_memory_end - m_available_memory_it) {
+ // slow path, only happens when a new chunk needs to be allocated
+ AllocateChunk();
+ }
+
+ // Make sure we use the right amount of bytes for that freelist (might be rounded up),
+ return std::exchange(m_available_memory_it, m_available_memory_it + round_bytes);
+ }
+
+ // Can't use the pool => use operator new()
+ return ::operator new (bytes, std::align_val_t{alignment});
+ }
+
+ /**
+ * Returns a block to the freelists, or deletes the block when it did not come from the chunks.
+ */
+ void Deallocate(void* p, std::size_t bytes, std::size_t alignment) noexcept
+ {
+ if (IsFreeListUsable(bytes, alignment)) {
+ const std::size_t num_alignments = NumElemAlignBytes(bytes);
+ // put the memory block into the linked list. We can placement construct the FreeList
+ // into the memory since we can be sure the alignment is correct.
+ PlacementAddToList(p, m_free_lists[num_alignments]);
+ } else {
+ // Can't use the pool => forward deallocation to ::operator delete().
+ ::operator delete (p, std::align_val_t{alignment});
+ }
+ }
+
+ /**
+ * Number of allocated chunks
+ */
+ [[nodiscard]] std::size_t NumAllocatedChunks() const
+ {
+ return m_allocated_chunks.size();
+ }
+
+ /**
+ * Size in bytes to allocate per chunk, currently hardcoded to a fixed size.
+ */
+ [[nodiscard]] size_t ChunkSizeBytes() const
+ {
+ return m_chunk_size_bytes;
+ }
+};
+
+
+/**
+ * Forwards all allocations/deallocations to the PoolResource.
+ */
+template <class T, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+class PoolAllocator
+{
+ PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>* m_resource;
+
+ template <typename U, std::size_t M, std::size_t A>
+ friend class PoolAllocator;
+
+public:
+ using value_type = T;
+ using ResourceType = PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>;
+
+ /**
+ * Not explicit so we can easily construct it with the correct resource
+ */
+ PoolAllocator(ResourceType* resource) noexcept
+ : m_resource(resource)
+ {
+ }
+
+ PoolAllocator(const PoolAllocator& other) noexcept = default;
+ PoolAllocator& operator=(const PoolAllocator& other) noexcept = default;
+
+ template <class U>
+ PoolAllocator(const PoolAllocator<U, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& other) noexcept
+ : m_resource(other.resource())
+ {
+ }
+
+ /**
+ * The rebind struct here is mandatory because we use non type template arguments for
+ * PoolAllocator. See https://en.cppreference.com/w/cpp/named_req/Allocator#cite_note-2
+ */
+ template <typename U>
+ struct rebind {
+ using other = PoolAllocator<U, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>;
+ };
+
+ /**
+ * Forwards each call to the resource.
+ */
+ T* allocate(size_t n)
+ {
+ return static_cast<T*>(m_resource->Allocate(n * sizeof(T), alignof(T)));
+ }
+
+ /**
+ * Forwards each call to the resource.
+ */
+ void deallocate(T* p, size_t n) noexcept
+ {
+ m_resource->Deallocate(p, n * sizeof(T), alignof(T));
+ }
+
+ ResourceType* resource() const noexcept
+ {
+ return m_resource;
+ }
+};
+
+template <class T1, class T2, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+bool operator==(const PoolAllocator<T1, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& a,
+ const PoolAllocator<T2, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& b) noexcept
+{
+ return a.resource() == b.resource();
+}
+
+template <class T1, class T2, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+bool operator!=(const PoolAllocator<T1, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& a,
+ const PoolAllocator<T2, MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& b) noexcept
+{
+ return !(a == b);
+}
+
+#endif // BITCOIN_SUPPORT_ALLOCATORS_POOL_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 758691cfde..54d923e4a4 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -127,46 +127,45 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
// the specified port to tried, but not the other.
addrman->Good(CAddress(addr1_port, NODE_NONE));
BOOST_CHECK_EQUAL(addrman->Size(), 2U);
- bool newOnly = true;
- auto addr_ret3 = addrman->Select(newOnly).first;
+ bool new_only = true;
+ auto addr_ret3 = addrman->Select(new_only).first;
BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
}
-
BOOST_AUTO_TEST_CASE(addrman_select)
{
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
+ BOOST_CHECK(!addrman->Select(false).first.IsValid());
+ BOOST_CHECK(!addrman->Select(true).first.IsValid());
CNetAddr source = ResolveIP("252.2.2.2");
- // Test: Select from new with 1 addr in new.
+ // Add 1 address to the new table
CService addr1 = ResolveService("250.1.1.1", 8333);
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
- bool newOnly = true;
- auto addr_ret1 = addrman->Select(newOnly).first;
- BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
+ BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr1);
+ BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
- // Test: move addr to tried, select from new expected nothing returned.
+ // Move address to the tried table
BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
- BOOST_CHECK_EQUAL(addrman->Size(), 1U);
- auto addr_ret2 = addrman->Select(newOnly).first;
- BOOST_CHECK_EQUAL(addr_ret2.ToStringAddrPort(), "[::]:0");
-
- auto addr_ret3 = addrman->Select().first;
- BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
+ BOOST_CHECK(!addrman->Select(/*new_only=*/true).first.IsValid());
+ BOOST_CHECK(addrman->Select().first == addr1);
+ BOOST_CHECK_EQUAL(addrman->Size(), 1U);
-
- // Add three addresses to new table.
+ // Add one address to the new table
CService addr2 = ResolveService("250.3.1.1", 8333);
+ BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, addr2));
+ BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr2);
+
+ // Add two more addresses to the new table
CService addr3 = ResolveService("250.3.2.2", 9999);
CService addr4 = ResolveService("250.3.3.3", 9999);
- BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
- BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, addr2));
BOOST_CHECK(addrman->Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
// Add three addresses to tried table.
@@ -174,17 +173,17 @@ BOOST_AUTO_TEST_CASE(addrman_select)
CService addr6 = ResolveService("250.4.5.5", 7777);
CService addr7 = ResolveService("250.4.6.6", 8333);
- BOOST_CHECK(addrman->Add({CAddress(addr5, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr5, NODE_NONE)}, addr3));
BOOST_CHECK(addrman->Good(CAddress(addr5, NODE_NONE)));
- BOOST_CHECK(addrman->Add({CAddress(addr6, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr6, NODE_NONE)}, addr3));
BOOST_CHECK(addrman->Good(CAddress(addr6, NODE_NONE)));
BOOST_CHECK(addrman->Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333)));
BOOST_CHECK(addrman->Good(CAddress(addr7, NODE_NONE)));
- // Test: 6 addrs + 1 addr from last test = 7.
+ // 6 addrs + 1 addr from last test = 7.
BOOST_CHECK_EQUAL(addrman->Size(), 7U);
- // Test: Select pulls from new and tried regardless of port number.
+ // Select pulls from new and tried regardless of port number.
std::set<uint16_t> ports;
for (int i = 0; i < 20; ++i) {
ports.insert(addrman->Select().first.GetPort());
@@ -192,6 +191,89 @@ BOOST_AUTO_TEST_CASE(addrman_select)
BOOST_CHECK_EQUAL(ports.size(), 3U);
}
+BOOST_AUTO_TEST_CASE(addrman_select_by_network)
+{
+ auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
+ BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_IPV4).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV4).first.IsValid());
+
+ // add ipv4 address to the new table
+ CNetAddr source = ResolveIP("252.2.2.2");
+ CService addr1 = ResolveService("250.1.1.1", 8333);
+ BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
+
+ BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_IPV4).first == addr1);
+ BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_I2P).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_CJDNS).first.IsValid());
+ BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
+
+ // add I2P address to the new table
+ CAddress i2p_addr;
+ i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
+ BOOST_CHECK(addrman->Add({i2p_addr}, source));
+
+ BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr);
+ BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
+ BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
+ BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
+
+ // bump I2P address to tried table
+ BOOST_CHECK(addrman->Good(i2p_addr));
+
+ BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_I2P).first.IsValid());
+ BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
+
+ // add another I2P address to the new table
+ CAddress i2p_addr2;
+ i2p_addr2.SetSpecial("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p");
+ BOOST_CHECK(addrman->Add({i2p_addr2}, source));
+
+ BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr2);
+
+ // ensure that both new and tried table are selected from
+ bool new_selected{false};
+ bool tried_selected{false};
+
+ while (!new_selected || !tried_selected) {
+ const CAddress selected{addrman->Select(/*new_only=*/false, NET_I2P).first};
+ BOOST_REQUIRE(selected == i2p_addr || selected == i2p_addr2);
+ if (selected == i2p_addr) {
+ tried_selected = true;
+ } else {
+ new_selected = true;
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(addrman_select_special)
+{
+ // use a non-deterministic addrman to ensure a passing test isn't due to setup
+ auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, /*deterministic=*/false, GetCheckRatio(m_node));
+
+ CNetAddr source = ResolveIP("252.2.2.2");
+
+ // add I2P address to the tried table
+ CAddress i2p_addr;
+ i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
+ BOOST_CHECK(addrman->Add({i2p_addr}, source));
+ BOOST_CHECK(addrman->Good(i2p_addr));
+
+ // add ipv4 address to the new table
+ CService addr1 = ResolveService("250.1.1.3", 8333);
+ BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
+
+ // since the only ipv4 address is on the new table, ensure that the new
+ // table gets selected even if new_only is false. if the table was being
+ // selected at random, this test will sporadically fail
+ BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
+}
+
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp
index 5bade94f37..6a0925f0bb 100644
--- a/src/test/argsman_tests.cpp
+++ b/src/test/argsman_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <sync.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
@@ -9,7 +10,6 @@
#include <univalue.h>
#include <util/fs.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <array>
#include <optional>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 6bc4770f9b..9011e703e8 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <checkqueue.h>
+#include <common/args.h>
#include <sync.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <util/time.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index e082800fc3..853dc6dc1e 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -6,6 +6,7 @@
#include <coins.h>
#include <script/standard.h>
#include <streams.h>
+#include <test/util/poolresourcetester.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <txdb.h>
@@ -612,7 +613,8 @@ void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags, const C
void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{
- CCoinsMap map;
+ CCoinsMapMemoryResource resource;
+ CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
InsertCoinsMapEntry(map, value, flags);
BOOST_CHECK(view.BatchWrite(map, {}));
}
@@ -911,6 +913,7 @@ void TestFlushBehavior(
CAmount value;
char flags;
size_t cache_usage;
+ size_t cache_size;
auto flush_all = [&all_caches](bool erase) {
// Flush in reverse order to ensure that flushes happen from children up.
@@ -935,6 +938,8 @@ void TestFlushBehavior(
view->AddCoin(outp, Coin(coin), false);
cache_usage = view->DynamicMemoryUsage();
+ cache_size = view->map().size();
+
// `base` shouldn't have coin (no flush yet) but `view` should have cached it.
BOOST_CHECK(!base.HaveCoin(outp));
BOOST_CHECK(view->HaveCoin(outp));
@@ -949,6 +954,7 @@ void TestFlushBehavior(
// CoinsMap usage should be unchanged since we didn't erase anything.
BOOST_CHECK_EQUAL(cache_usage, view->DynamicMemoryUsage());
+ BOOST_CHECK_EQUAL(cache_size, view->map().size());
// --- 3. Ensuring the entry still exists in the cache and has been written to parent
//
@@ -965,8 +971,10 @@ void TestFlushBehavior(
//
flush_all(/*erase=*/ true);
- // Memory usage should have gone down.
- BOOST_CHECK(view->DynamicMemoryUsage() < cache_usage);
+ // Memory does not necessarily go down due to the map using a memory pool
+ BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage);
+ // Size of the cache must go down though
+ BOOST_TEST(view->map().size() < cache_size);
// --- 5. Ensuring the entry is no longer in the cache
//
@@ -1076,4 +1084,29 @@ BOOST_AUTO_TEST_CASE(ccoins_flush_behavior)
}
}
+BOOST_AUTO_TEST_CASE(coins_resource_is_used)
+{
+ CCoinsMapMemoryResource resource;
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+
+ {
+ CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
+ BOOST_TEST(memusage::DynamicUsage(map) >= resource.ChunkSizeBytes());
+
+ map.reserve(1000);
+
+ // The resource has preallocated a chunk, so we should have space for at several nodes without the need to allocate anything else.
+ const auto usage_before = memusage::DynamicUsage(map);
+
+ COutPoint out_point{};
+ for (size_t i = 0; i < 1000; ++i) {
+ out_point.n = i;
+ map[out_point];
+ }
+ BOOST_TEST(usage_before == memusage::DynamicUsage(map));
+ }
+
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index aca2b8eff0..1a06f16155 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -6,6 +6,7 @@
#include <banman.h>
#include <chainparams.h>
+#include <common/args.h>
#include <net.h>
#include <net_processing.h>
#include <pubkey.h>
@@ -17,7 +18,6 @@
#include <test/util/setup_common.h>
#include <timedata.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/time.h>
#include <validation.h>
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 54c30ed314..3874b38f61 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <clientversion.h>
+#include <common/args.h>
#include <flatfile.h>
#include <streams.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index a59e41dbb5..5ad7a25c53 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -6,6 +6,7 @@
#include <addrman.h>
#include <addrman_impl.h>
#include <chainparams.h>
+#include <common/args.h>
#include <merkleblock.h>
#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -15,7 +16,6 @@
#include <test/util/setup_common.h>
#include <time.h>
#include <util/asmap.h>
-#include <util/system.h>
#include <cassert>
#include <cstdint>
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index b4a93499ed..cb5d29d9b8 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <banman.h>
+#include <common/args.h>
#include <netaddress.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -11,7 +12,6 @@
#include <test/util/setup_common.h>
#include <util/fs.h>
#include <util/readwritefile.h>
-#include <util/system.h>
#include <cassert>
#include <cstdint>
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index e80c772aa4..5843b80c0a 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -115,7 +115,8 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
random_mutable_transaction = *opt_mutable_transaction;
},
[&] {
- CCoinsMap coins_map;
+ CCoinsMapMemoryResource resource;
+ CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CCoinsCacheEntry coins_cache_entry;
coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 7ce17c0b7c..0f3c850e66 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -5,6 +5,7 @@
#include <addrman.h>
#include <chainparams.h>
#include <chainparamsbase.h>
+#include <common/args.h>
#include <net.h>
#include <netaddress.h>
#include <protocol.h>
@@ -13,7 +14,6 @@
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <util/translation.h>
#include <cstdint>
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 7cd78e0461..177711c6e4 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -9,6 +9,7 @@
#include <blockfilter.h>
#include <chain.h>
#include <coins.h>
+#include <common/args.h>
#include <compressor.h>
#include <consensus/merkle.h>
#include <key.h>
@@ -25,7 +26,6 @@
#include <streams.h>
#include <test/util/setup_common.h>
#include <undo.h>
-#include <util/system.h>
#include <version.h>
#include <exception>
diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp
index 6c2321cd68..3c6db96446 100644
--- a/src/test/fuzz/i2p.cpp
+++ b/src/test/fuzz/i2p.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <i2p.h>
#include <netaddress.h>
#include <netbase.h>
@@ -10,7 +11,6 @@
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <util/threadinterrupt.h>
void initialize_i2p()
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index c0aefe6067..ead877fe05 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <arith_uint256.h>
+#include <common/args.h>
#include <compressor.h>
#include <consensus/amount.h>
#include <consensus/merkle.h>
diff --git a/src/test/fuzz/poolresource.cpp b/src/test/fuzz/poolresource.cpp
new file mode 100644
index 0000000000..ce64ef6472
--- /dev/null
+++ b/src/test/fuzz/poolresource.cpp
@@ -0,0 +1,174 @@
+// Copyright (c) 2022 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 <span.h>
+#include <support/allocators/pool.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/poolresourcetester.h>
+#include <test/util/xoroshiro128plusplus.h>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+namespace {
+
+template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+class PoolResourceFuzzer
+{
+ FuzzedDataProvider& m_provider;
+ PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES> m_test_resource;
+ uint64_t m_sequence{0};
+ size_t m_total_allocated{};
+
+ struct Entry {
+ Span<std::byte> span;
+ size_t alignment;
+ uint64_t seed;
+
+ Entry(Span<std::byte> s, size_t a, uint64_t se) : span(s), alignment(a), seed(se) {}
+ };
+
+ std::vector<Entry> m_entries;
+
+public:
+ PoolResourceFuzzer(FuzzedDataProvider& provider)
+ : m_provider{provider},
+ m_test_resource{provider.ConsumeIntegralInRange<size_t>(MAX_BLOCK_SIZE_BYTES, 262144)}
+ {
+ }
+
+ void Allocate(size_t size, size_t alignment)
+ {
+ assert(size > 0); // Must allocate at least 1 byte.
+ assert(alignment > 0); // Alignment must be at least 1.
+ assert((alignment & (alignment - 1)) == 0); // Alignment must be power of 2.
+ assert((size & (alignment - 1)) == 0); // Size must be a multiple of alignment.
+
+ auto span = Span(static_cast<std::byte*>(m_test_resource.Allocate(size, alignment)), size);
+ m_total_allocated += size;
+
+ auto ptr_val = reinterpret_cast<std::uintptr_t>(span.data());
+ assert((ptr_val & (alignment - 1)) == 0);
+
+ uint64_t seed = m_sequence++;
+ RandomContentFill(m_entries.emplace_back(span, alignment, seed));
+ }
+
+ void
+ Allocate()
+ {
+ if (m_total_allocated > 0x1000000) return;
+ size_t alignment_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 7);
+ size_t alignment = 1 << alignment_bits;
+ size_t size_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 16 - alignment_bits);
+ size_t size = m_provider.ConsumeIntegralInRange<size_t>(1U << size_bits, (1U << (size_bits + 1)) - 1U) << alignment_bits;
+ Allocate(size, alignment);
+ }
+
+ void RandomContentFill(Entry& entry)
+ {
+ XoRoShiRo128PlusPlus rng(entry.seed);
+ auto ptr = entry.span.data();
+ auto size = entry.span.size();
+
+ while (size >= 8) {
+ auto r = rng();
+ std::memcpy(ptr, &r, 8);
+ size -= 8;
+ ptr += 8;
+ }
+ if (size > 0) {
+ auto r = rng();
+ std::memcpy(ptr, &r, size);
+ }
+ }
+
+ void RandomContentCheck(const Entry& entry)
+ {
+ XoRoShiRo128PlusPlus rng(entry.seed);
+ auto ptr = entry.span.data();
+ auto size = entry.span.size();
+
+ std::byte buf[8];
+ while (size >= 8) {
+ auto r = rng();
+ std::memcpy(buf, &r, 8);
+ assert(std::memcmp(buf, ptr, 8) == 0);
+ size -= 8;
+ ptr += 8;
+ }
+ if (size > 0) {
+ auto r = rng();
+ std::memcpy(buf, &r, size);
+ assert(std::memcmp(buf, ptr, size) == 0);
+ }
+ }
+
+ void Deallocate(const Entry& entry)
+ {
+ auto ptr_val = reinterpret_cast<std::uintptr_t>(entry.span.data());
+ assert((ptr_val & (entry.alignment - 1)) == 0);
+ RandomContentCheck(entry);
+ m_total_allocated -= entry.span.size();
+ m_test_resource.Deallocate(entry.span.data(), entry.span.size(), entry.alignment);
+ }
+
+ void Deallocate()
+ {
+ if (m_entries.empty()) {
+ return;
+ }
+
+ size_t idx = m_provider.ConsumeIntegralInRange<size_t>(0, m_entries.size() - 1);
+ Deallocate(m_entries[idx]);
+ if (idx != m_entries.size() - 1) {
+ m_entries[idx] = std::move(m_entries.back());
+ }
+ m_entries.pop_back();
+ }
+
+ void Clear()
+ {
+ while (!m_entries.empty()) {
+ Deallocate();
+ }
+
+ PoolResourceTester::CheckAllDataAccountedFor(m_test_resource);
+ }
+
+ void Fuzz()
+ {
+ LIMITED_WHILE(m_provider.ConsumeBool(), 10000)
+ {
+ CallOneOf(
+ m_provider,
+ [&] { Allocate(); },
+ [&] { Deallocate(); });
+ }
+ Clear();
+ }
+};
+
+
+} // namespace
+
+FUZZ_TARGET(pool_resource)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ CallOneOf(
+ provider,
+ [&] { PoolResourceFuzzer<128, 1>{provider}.Fuzz(); },
+ [&] { PoolResourceFuzzer<128, 2>{provider}.Fuzz(); },
+ [&] { PoolResourceFuzzer<128, 4>{provider}.Fuzz(); },
+ [&] { PoolResourceFuzzer<128, 8>{provider}.Fuzz(); },
+
+ [&] { PoolResourceFuzzer<8, 8>{provider}.Fuzz(); },
+ [&] { PoolResourceFuzzer<16, 16>{provider}.Fuzz(); },
+
+ [&] { PoolResourceFuzzer<256, alignof(max_align_t)>{provider}.Fuzz(); },
+ [&] { PoolResourceFuzzer<256, 64>{provider}.Fuzz(); });
+}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 2578137471..1c6140c66a 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -70,7 +70,6 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
"addconnection", // avoid DNS lookups
"addnode", // avoid DNS lookups
"addpeeraddress", // avoid DNS lookups
- "analyzepsbt", // avoid signed integer overflow in CFeeRate::GetFee(unsigned long) (https://github.com/bitcoin/bitcoin/issues/20607)
"dumptxoutset", // avoid writing to disk
"dumpwallet", // avoid writing to disk
"echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
@@ -79,7 +78,6 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
"gettxoutproof", // avoid prohibitively slow execution
"importwallet", // avoid reading from disk
"loadwallet", // avoid reading from disk
- "prioritisetransaction", // avoid signed integer overflow in CTxMemPool::PrioritiseTransaction(uint256 const&, long const&) (https://github.com/bitcoin/bitcoin/issues/20626)
"savemempool", // disabled as a precautionary measure: may take a file path argument in the future
"setban", // avoid DNS lookups
"stop", // avoid shutdown state
@@ -87,6 +85,7 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
// RPC commands which are safe for fuzzing.
const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
+ "analyzepsbt",
"clearbanned",
"combinepsbt",
"combinerawtransaction",
@@ -112,9 +111,9 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getblockchaininfo",
"getblockcount",
"getblockfilter",
+ "getblockfrompeer", // when no peers are connected, no p2p message is sent
"getblockhash",
"getblockheader",
- "getblockfrompeer", // when no peers are connected, no p2p message is sent
"getblockstats",
"getblocktemplate",
"getchaintips",
@@ -128,7 +127,6 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getmempoolancestors",
"getmempooldescendants",
"getmempoolentry",
- "gettxspendingprevout",
"getmempoolinfo",
"getmininginfo",
"getnettotals",
@@ -141,6 +139,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getrpcinfo",
"gettxout",
"gettxoutsetinfo",
+ "gettxspendingprevout",
"help",
"invalidateblock",
"joinpsbts",
@@ -149,6 +148,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"mockscheduler",
"ping",
"preciousblock",
+ "prioritisetransaction",
"pruneblockchain",
"reconsiderblock",
"scanblocks",
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index c7363f8f49..5de24a939d 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -4,6 +4,7 @@
#include <blockfilter.h>
#include <clientversion.h>
+#include <common/args.h>
#include <common/url.h>
#include <netbase.h>
#include <outputtype.h>
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index dc3f9c8b8f..935f0c21e1 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#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>
#include <string>
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index 143027662f..e6a19d6e91 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -4,9 +4,9 @@
#include <chain.h>
#include <chainparams.h>
+#include <common/args.h>
#include <consensus/params.h>
#include <primitives/block.h>
-#include <util/system.h>
#include <versionbits.h>
#include <test/fuzz/FuzzedDataProvider.h>
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 9106040b84..715b6885f5 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -2,11 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
+#include <logging.h>
#include <test/util/setup_common.h>
#include <univalue.h>
#include <util/settings.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <limits>
#include <string>
diff --git a/src/test/httpserver_tests.cpp b/src/test/httpserver_tests.cpp
index ee59ec6967..c95a777e80 100644
--- a/src/test/httpserver_tests.cpp
+++ b/src/test/httpserver_tests.cpp
@@ -34,5 +34,9 @@ BOOST_AUTO_TEST_CASE(test_query_parameters)
// Invalid query string syntax is the same as not having parameters
uri = "/rest/endpoint/someresource.json&p1=v1&p2=v2";
BOOST_CHECK(!GetQueryParameterFromUri(uri.c_str(), "p1").has_value());
+
+ // URI with invalid characters (%) raises a runtime error regardless of which query parameter is queried
+ uri = "/rest/endpoint/someresource.json&p1=v1&p2=v2%";
+ BOOST_CHECK_EXCEPTION(GetQueryParameterFromUri(uri.c_str(), "p1"), std::runtime_error, HasReason("URI parsing failed, it likely contained RFC 3986 invalid characters"));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index 3e20b527b5..b2e1ae43be 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
#include <test/util/logging.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <util/threadinterrupt.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 4fbd9b3a6e..631c213627 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <clientversion.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <cstdint>
#include <net.h>
@@ -19,7 +20,6 @@
#include <timedata.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <validation.h>
#include <version.h>
diff --git a/src/test/pool_tests.cpp b/src/test/pool_tests.cpp
new file mode 100644
index 0000000000..8a07e09a44
--- /dev/null
+++ b/src/test/pool_tests.cpp
@@ -0,0 +1,190 @@
+// Copyright (c) 2022 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 <memusage.h>
+#include <support/allocators/pool.h>
+#include <test/util/poolresourcetester.h>
+#include <test/util/random.h>
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <cstddef>
+#include <cstdint>
+#include <unordered_map>
+#include <vector>
+
+BOOST_FIXTURE_TEST_SUITE(pool_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(basic_allocating)
+{
+ auto resource = PoolResource<8, 8>();
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+
+ // first chunk is already allocated
+ size_t expected_bytes_available = resource.ChunkSizeBytes();
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ // chunk is used, no more allocation
+ void* block = resource.Allocate(8, 8);
+ expected_bytes_available -= 8;
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);
+ resource.Deallocate(block, 8, 8);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+
+ // alignment is too small, but the best fitting freelist is used. Nothing is allocated.
+ void* b = resource.Allocate(8, 1);
+ BOOST_TEST(b == block); // we got the same block of memory as before
+ BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ resource.Deallocate(block, 8, 1);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ // can't use resource because alignment is too big, allocate system memory
+ b = resource.Allocate(8, 16);
+ BOOST_TEST(b != block);
+ block = b;
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ resource.Deallocate(block, 8, 16);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ // can't use chunk because size is too big
+ block = resource.Allocate(16, 8);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ resource.Deallocate(block, 16, 8);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ // it's possible that 0 bytes are allocated, make sure this works. In that case the call is forwarded to operator new
+ // 0 bytes takes one entry from the first freelist
+ void* p = resource.Allocate(0, 1);
+ BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+
+ resource.Deallocate(p, 0, 1);
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+ BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);
+ BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));
+}
+
+// Allocates from 0 to n bytes were n > the PoolResource's data, and each should work
+BOOST_AUTO_TEST_CASE(allocate_any_byte)
+{
+ auto resource = PoolResource<128, 8>(1024);
+
+ uint8_t num_allocs = 200;
+
+ auto data = std::vector<Span<uint8_t>>();
+
+ // allocate an increasing number of bytes
+ for (uint8_t num_bytes = 0; num_bytes < num_allocs; ++num_bytes) {
+ uint8_t* bytes = new (resource.Allocate(num_bytes, 1)) uint8_t[num_bytes];
+ BOOST_TEST(bytes != nullptr);
+ data.emplace_back(bytes, num_bytes);
+
+ // set each byte to num_bytes
+ std::fill(bytes, bytes + num_bytes, num_bytes);
+ }
+
+ // now that we got all allocated, test if all still have the correct values, and give everything back to the allocator
+ uint8_t val = 0;
+ for (auto const& span : data) {
+ for (auto x : span) {
+ BOOST_TEST(val == x);
+ }
+ std::destroy(span.data(), span.data() + span.size());
+ resource.Deallocate(span.data(), span.size(), 1);
+ ++val;
+ }
+
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+}
+
+BOOST_AUTO_TEST_CASE(random_allocations)
+{
+ struct PtrSizeAlignment {
+ void* ptr;
+ size_t bytes;
+ size_t alignment;
+ };
+
+ // makes a bunch of random allocations and gives all of them back in random order.
+ auto resource = PoolResource<128, 8>(65536);
+ std::vector<PtrSizeAlignment> ptr_size_alignment{};
+ for (size_t i = 0; i < 1000; ++i) {
+ // make it a bit more likely to allocate than deallocate
+ if (ptr_size_alignment.empty() || 0 != InsecureRandRange(4)) {
+ // allocate a random item
+ std::size_t alignment = std::size_t{1} << InsecureRandRange(8); // 1, 2, ..., 128
+ std::size_t size = (InsecureRandRange(200) / alignment + 1) * alignment; // multiple of alignment
+ void* ptr = resource.Allocate(size, alignment);
+ BOOST_TEST(ptr != nullptr);
+ BOOST_TEST((reinterpret_cast<uintptr_t>(ptr) & (alignment - 1)) == 0);
+ ptr_size_alignment.push_back({ptr, size, alignment});
+ } else {
+ // deallocate a random item
+ auto& x = ptr_size_alignment[InsecureRandRange(ptr_size_alignment.size())];
+ resource.Deallocate(x.ptr, x.bytes, x.alignment);
+ x = ptr_size_alignment.back();
+ ptr_size_alignment.pop_back();
+ }
+ }
+
+ // deallocate all the rest
+ for (auto const& x : ptr_size_alignment) {
+ resource.Deallocate(x.ptr, x.bytes, x.alignment);
+ }
+
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+}
+
+BOOST_AUTO_TEST_CASE(memusage_test)
+{
+ auto std_map = std::unordered_map<int, int>{};
+
+ using Map = std::unordered_map<int,
+ int,
+ std::hash<int>,
+ std::equal_to<int>,
+ PoolAllocator<std::pair<const int, int>,
+ sizeof(std::pair<const int, int>) + sizeof(void*) * 4,
+ alignof(void*)>>;
+ auto resource = Map::allocator_type::ResourceType(1024);
+
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+
+ {
+ auto resource_map = Map{0, std::hash<int>{}, std::equal_to<int>{}, &resource};
+
+ // can't have the same resource usage
+ BOOST_TEST(memusage::DynamicUsage(std_map) != memusage::DynamicUsage(resource_map));
+
+ for (size_t i = 0; i < 10000; ++i) {
+ std_map[i];
+ resource_map[i];
+ }
+
+ // Eventually the resource_map should have a much lower memory usage because it has less malloc overhead
+ BOOST_TEST(memusage::DynamicUsage(resource_map) <= memusage::DynamicUsage(std_map) * 90 / 100);
+ }
+
+ PoolResourceTester::CheckAllDataAccountedFor(resource);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index e5cf767614..414e4509f5 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -5,6 +5,7 @@
#include <random.h>
#include <test/util/setup_common.h>
+#include <util/time.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 4d0d42ae38..604eb48df6 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -10,10 +10,10 @@
#include <boost/test/unit_test.hpp>
+#include <common/args.h>
#include <univalue.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <fstream>
#include <map>
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index 024526497c..c08d2748a6 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -16,6 +16,9 @@
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(txpackage_tests)
+// A fee amount that is above 1sat/vB but below 5sat/vB for most transactions created within these
+// unit tests.
+static const CAmount low_fee_amt{200};
// Create placeholder transactions that have no meaning.
inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
@@ -373,6 +376,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
{
// Mine blocks to mature coinbases.
mineBlocks(5);
+ MockMempoolMinFee(CFeeRate(5000));
LOCK(cs_main);
// Transactions with a same-txid-different-witness transaction in the mempool should be ignored,
@@ -560,13 +564,15 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
BOOST_CHECK(parent2_v2_result.m_result_type == MempoolAcceptResult::ResultType::VALID);
package_mixed.push_back(ptx_parent2_v1);
- // parent3 will be a new transaction. Put 0 fees on it to make it invalid on its own.
+ // parent3 will be a new transaction. Put a low feerate to make it invalid on its own.
auto mtx_parent3 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[3], /*input_vout=*/0,
/*input_height=*/0, /*input_signing_key=*/coinbaseKey,
/*output_destination=*/acs_spk,
- /*output_amount=*/CAmount(50 * COIN), /*submit=*/false);
+ /*output_amount=*/CAmount(50 * COIN - low_fee_amt), /*submit=*/false);
CTransactionRef ptx_parent3 = MakeTransactionRef(mtx_parent3);
package_mixed.push_back(ptx_parent3);
+ BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*ptx_parent3)) > low_fee_amt);
+ BOOST_CHECK(m_node.mempool->m_min_relay_feerate.GetFee(GetVirtualTransactionSize(*ptx_parent3)) <= low_fee_amt);
// child spends parent1, parent2, and parent3
CKey mixed_grandchild_key;
@@ -627,6 +633,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
{
mineBlocks(5);
+ MockMempoolMinFee(CFeeRate(5000));
LOCK(::cs_main);
size_t expected_pool_size = m_node.mempool->size();
CKey child_key;
@@ -636,9 +643,9 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
grandchild_key.MakeNewKey(true);
CScript child_spk = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));
- // zero-fee parent and high-fee child package
+ // low-fee parent and high-fee child package
const CAmount coinbase_value{50 * COIN};
- const CAmount parent_value{coinbase_value - 0};
+ const CAmount parent_value{coinbase_value - low_fee_amt};
const CAmount child_value{parent_value - COIN};
Package package_cpfp;
@@ -657,17 +664,20 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
package_cpfp.push_back(tx_child);
// Package feerate is calculated using modified fees, and prioritisetransaction accepts negative
- // fee deltas. This should be taken into account. De-prioritise the parent transaction by -1BTC,
- // bringing the package feerate to 0.
- m_node.mempool->PrioritiseTransaction(tx_parent->GetHash(), -1 * COIN);
+ // fee deltas. This should be taken into account. De-prioritise the parent transaction
+ // to bring the package feerate to 0.
+ m_node.mempool->PrioritiseTransaction(tx_parent->GetHash(), child_value - coinbase_value);
{
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const auto submit_cpfp_deprio = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
package_cpfp, /*test_accept=*/ false);
- BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
- BOOST_CHECK_MESSAGE(submit_cpfp_deprio.m_state.IsInvalid(),
- "Package validation unexpectedly succeeded: " << submit_cpfp_deprio.m_state.GetRejectReason());
- BOOST_CHECK(submit_cpfp_deprio.m_tx_results.empty());
+ BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_state.GetResult(), PackageValidationResult::PCKG_TX);
+ BOOST_CHECK(submit_cpfp_deprio.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetResult(),
+ TxValidationResult::TX_MEMPOOL_POLICY);
+ BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_child->GetWitnessHash())->second.m_state.GetResult(),
+ TxValidationResult::TX_MISSING_INPUTS);
+ BOOST_CHECK(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetRejectReason() == "min relay fee not met");
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const CFeeRate expected_feerate(0, GetVirtualTransactionSize(*tx_parent) + GetVirtualTransactionSize(*tx_child));
}
@@ -675,8 +685,8 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
// Clear the prioritisation of the parent transaction.
WITH_LOCK(m_node.mempool->cs, m_node.mempool->ClearPrioritisation(tx_parent->GetHash()));
- // Package CPFP: Even though the parent pays 0 absolute fees, the child pays 1 BTC which is
- // enough for the package feerate to meet the threshold.
+ // Package CPFP: Even though the parent's feerate is below the mempool minimum feerate, the
+ // child pays enough for the package feerate to meet the threshold.
{
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const auto submit_cpfp = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
@@ -689,7 +699,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
auto it_child = submit_cpfp.m_tx_results.find(tx_child->GetWitnessHash());
BOOST_CHECK(it_parent != submit_cpfp.m_tx_results.end());
BOOST_CHECK(it_parent->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
- BOOST_CHECK(it_parent->second.m_base_fees.value() == 0);
+ BOOST_CHECK(it_parent->second.m_base_fees.value() == coinbase_value - parent_value);
BOOST_CHECK(it_child != submit_cpfp.m_tx_results.end());
BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
BOOST_CHECK(it_child->second.m_base_fees.value() == COIN);
@@ -709,22 +719,28 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
}
// Just because we allow low-fee parents doesn't mean we allow low-feerate packages.
- // This package just pays 200 satoshis total. This would be enough to pay for the child alone,
- // but isn't enough for the entire package to meet the 1sat/vbyte minimum.
+ // The mempool minimum feerate is 5sat/vB, but this package just pays 800 satoshis total.
+ // The child fees would be able to pay for itself, but isn't enough for the entire package.
Package package_still_too_low;
+ const CAmount parent_fee{200};
+ const CAmount child_fee{600};
auto mtx_parent_cheap = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0,
/*input_height=*/0, /*input_signing_key=*/coinbaseKey,
/*output_destination=*/parent_spk,
- /*output_amount=*/coinbase_value, /*submit=*/false);
+ /*output_amount=*/coinbase_value - parent_fee, /*submit=*/false);
CTransactionRef tx_parent_cheap = MakeTransactionRef(mtx_parent_cheap);
package_still_too_low.push_back(tx_parent_cheap);
+ BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_parent_cheap)) > parent_fee);
+ BOOST_CHECK(m_node.mempool->m_min_relay_feerate.GetFee(GetVirtualTransactionSize(*tx_parent_cheap)) <= parent_fee);
auto mtx_child_cheap = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent_cheap, /*input_vout=*/0,
/*input_height=*/101, /*input_signing_key=*/child_key,
/*output_destination=*/child_spk,
- /*output_amount=*/coinbase_value - 200, /*submit=*/false);
+ /*output_amount=*/coinbase_value - parent_fee - child_fee, /*submit=*/false);
CTransactionRef tx_child_cheap = MakeTransactionRef(mtx_child_cheap);
package_still_too_low.push_back(tx_child_cheap);
+ BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_child_cheap)) <= child_fee);
+ BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap)) > parent_fee + child_fee);
// Cheap package should fail with package-fee-too-low.
{
@@ -735,11 +751,6 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
BOOST_CHECK_EQUAL(submit_package_too_low.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(submit_package_too_low.m_state.GetRejectReason(), "package-fee-too-low");
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
- const CFeeRate child_feerate(200, GetVirtualTransactionSize(*tx_child_cheap));
- BOOST_CHECK(child_feerate.GetFeePerK() > 1000);
- const CFeeRate expected_feerate(200,
- GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap));
- BOOST_CHECK(expected_feerate.GetFeePerK() < 1000);
}
// Package feerate includes the modified fees of the transactions.
@@ -752,18 +763,18 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
expected_pool_size += 2;
BOOST_CHECK_MESSAGE(submit_prioritised_package.m_state.IsValid(),
"Package validation unexpectedly failed" << submit_prioritised_package.m_state.GetRejectReason());
- const CFeeRate expected_feerate(1 * COIN + 200,
+ const CFeeRate expected_feerate(1 * COIN + parent_fee + child_fee,
GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap));
BOOST_CHECK_EQUAL(submit_prioritised_package.m_tx_results.size(), package_still_too_low.size());
auto it_parent = submit_prioritised_package.m_tx_results.find(tx_parent_cheap->GetWitnessHash());
auto it_child = submit_prioritised_package.m_tx_results.find(tx_child_cheap->GetWitnessHash());
BOOST_CHECK(it_parent != submit_prioritised_package.m_tx_results.end());
BOOST_CHECK(it_parent->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
- BOOST_CHECK(it_parent->second.m_base_fees.value() == 0);
+ BOOST_CHECK(it_parent->second.m_base_fees.value() == parent_fee);
BOOST_CHECK(it_parent->second.m_effective_feerate.value() == expected_feerate);
BOOST_CHECK(it_child != submit_prioritised_package.m_tx_results.end());
BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
- BOOST_CHECK(it_child->second.m_base_fees.value() == 200);
+ BOOST_CHECK(it_child->second.m_base_fees.value() == child_fee);
BOOST_CHECK(it_child->second.m_effective_feerate.value() == expected_feerate);
std::vector<uint256> expected_wtxids({tx_parent_cheap->GetWitnessHash(), tx_child_cheap->GetWitnessHash()});
BOOST_CHECK(it_parent->second.m_wtxids_fee_calculations.value() == expected_wtxids);
@@ -800,8 +811,8 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
BOOST_CHECK_MESSAGE(submit_rich_parent.m_state.IsInvalid(), "Package validation unexpectedly succeeded");
// The child would have been validated on its own and failed, then submitted as a "package" of 1.
- BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
- BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetRejectReason(), "package-fee-too-low");
+ BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetResult(), PackageValidationResult::PCKG_TX);
+ BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetRejectReason(), "transaction failed");
auto it_parent = submit_rich_parent.m_tx_results.find(tx_parent_rich->GetWitnessHash());
BOOST_CHECK(it_parent != submit_rich_parent.m_tx_results.end());
@@ -810,6 +821,11 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
BOOST_CHECK_MESSAGE(it_parent->second.m_base_fees.value() == high_parent_fee,
strprintf("rich parent: expected fee %s, got %s", high_parent_fee, it_parent->second.m_base_fees.value()));
BOOST_CHECK(it_parent->second.m_effective_feerate == CFeeRate(high_parent_fee, GetVirtualTransactionSize(*tx_parent_rich)));
+ auto it_child = submit_rich_parent.m_tx_results.find(tx_child_poor->GetWitnessHash());
+ BOOST_CHECK(it_child != submit_rich_parent.m_tx_results.end());
+ BOOST_CHECK_EQUAL(it_child->second.m_result_type, MempoolAcceptResult::ResultType::INVALID);
+ BOOST_CHECK_EQUAL(it_child->second.m_state.GetResult(), TxValidationResult::TX_MEMPOOL_POLICY);
+ BOOST_CHECK(it_child->second.m_state.GetRejectReason() == "min relay fee not met");
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_parent_rich->GetHash())));
diff --git a/src/test/util/poolresourcetester.h b/src/test/util/poolresourcetester.h
new file mode 100644
index 0000000000..93f62eb2a9
--- /dev/null
+++ b/src/test/util/poolresourcetester.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
+#define BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
+
+#include <support/allocators/pool.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+
+/**
+ * Helper to get access to private parts of PoolResource. Used in unit tests and in the fuzzer
+ */
+class PoolResourceTester
+{
+ struct PtrAndBytes {
+ uintptr_t ptr;
+ std::size_t size;
+
+ PtrAndBytes(const void* p, std::size_t s)
+ : ptr(reinterpret_cast<uintptr_t>(p)), size(s)
+ {
+ }
+
+ /**
+ * defines a sort ordering by the pointer value
+ */
+ friend bool operator<(PtrAndBytes const& a, PtrAndBytes const& b)
+ {
+ return a.ptr < b.ptr;
+ }
+ };
+
+public:
+ /**
+ * Extracts the number of elements per freelist
+ */
+ template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+ static std::vector<std::size_t> FreeListSizes(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
+ {
+ auto sizes = std::vector<std::size_t>();
+ for (const auto* ptr : resource.m_free_lists) {
+ size_t size = 0;
+ while (ptr != nullptr) {
+ ++size;
+ ptr = ptr->m_next;
+ }
+ sizes.push_back(size);
+ }
+ return sizes;
+ }
+
+ /**
+ * How many bytes are still available from the last allocated chunk
+ */
+ template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+ static std::size_t AvailableMemoryFromChunk(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
+ {
+ return resource.m_available_memory_end - resource.m_available_memory_it;
+ }
+
+ /**
+ * Once all blocks are given back to the resource, tests that the freelists are consistent:
+ *
+ * * All data in the freelists must come from the chunks
+ * * Memory doesn't overlap
+ * * Each byte in the chunks can be accounted for in either the freelist or as available bytes.
+ */
+ template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
+ static void CheckAllDataAccountedFor(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
+ {
+ // collect all free blocks by iterating all freelists
+ std::vector<PtrAndBytes> free_blocks;
+ for (std::size_t freelist_idx = 0; freelist_idx < resource.m_free_lists.size(); ++freelist_idx) {
+ std::size_t bytes = freelist_idx * resource.ELEM_ALIGN_BYTES;
+ auto* ptr = resource.m_free_lists[freelist_idx];
+ while (ptr != nullptr) {
+ free_blocks.emplace_back(ptr, bytes);
+ ptr = ptr->m_next;
+ }
+ }
+ // also add whatever has not yet been used for blocks
+ auto num_available_bytes = resource.m_available_memory_end - resource.m_available_memory_it;
+ if (num_available_bytes > 0) {
+ free_blocks.emplace_back(resource.m_available_memory_it, num_available_bytes);
+ }
+
+ // collect all chunks
+ std::vector<PtrAndBytes> chunks;
+ for (const std::byte* ptr : resource.m_allocated_chunks) {
+ chunks.emplace_back(ptr, resource.ChunkSizeBytes());
+ }
+
+ // now we have all the data from all freelists on the one hand side, and all chunks on the other hand side.
+ // To check if all of them match, sort by address and iterate.
+ std::sort(free_blocks.begin(), free_blocks.end());
+ std::sort(chunks.begin(), chunks.end());
+
+ auto chunk_it = chunks.begin();
+ auto chunk_ptr_remaining = chunk_it->ptr;
+ auto chunk_size_remaining = chunk_it->size;
+ for (const auto& free_block : free_blocks) {
+ if (chunk_size_remaining == 0) {
+ assert(chunk_it != chunks.end());
+ ++chunk_it;
+ assert(chunk_it != chunks.end());
+ chunk_ptr_remaining = chunk_it->ptr;
+ chunk_size_remaining = chunk_it->size;
+ }
+ assert(free_block.ptr == chunk_ptr_remaining); // ensure addresses match
+ assert(free_block.size <= chunk_size_remaining); // ensure no overflow
+ assert((free_block.ptr & (resource.ELEM_ALIGN_BYTES - 1)) == 0); // ensure correct alignment
+ chunk_ptr_remaining += free_block.size;
+ chunk_size_remaining -= free_block.size;
+ }
+ // ensure we are at the end of the chunks
+ assert(chunk_ptr_remaining == chunk_it->ptr + chunk_it->size);
+ ++chunk_it;
+ assert(chunk_it == chunks.end());
+ assert(chunk_size_remaining == 0);
+ }
+};
+
+#endif // BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 58593c9d5b..ddc1e7ab37 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -44,6 +44,7 @@
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/system.h>
#include <util/thread.h>
#include <util/threadnames.h>
#include <util/time.h>
@@ -432,6 +433,33 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex
return mempool_transactions;
}
+void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
+{
+ LOCK2(cs_main, m_node.mempool->cs);
+ // Transactions in the mempool will affect the new minimum feerate.
+ assert(m_node.mempool->size() == 0);
+ // The target feerate cannot be too low...
+ // ...otherwise the transaction's feerate will need to be negative.
+ assert(target_feerate > m_node.mempool->m_incremental_relay_feerate);
+ // ...otherwise this is not meaningful. The feerate policy uses the maximum of both feerates.
+ assert(target_feerate > m_node.mempool->m_min_relay_feerate);
+
+ // Manually create an invalid transaction. Manually set the fee in the CTxMemPoolEntry to
+ // achieve the exact target feerate.
+ CMutableTransaction mtx = CMutableTransaction();
+ mtx.vin.push_back(CTxIn{COutPoint{g_insecure_rand_ctx.rand256(), 0}});
+ mtx.vout.push_back(CTxOut(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE))));
+ const auto tx{MakeTransactionRef(mtx)};
+ LockPoints lp;
+ // The new mempool min feerate is equal to the removed package's feerate + incremental feerate.
+ const auto tx_fee = target_feerate.GetFee(GetVirtualTransactionSize(*tx)) -
+ m_node.mempool->m_incremental_relay_feerate.GetFee(GetVirtualTransactionSize(*tx));
+ m_node.mempool->addUnchecked(CTxMemPoolEntry(tx, /*fee=*/tx_fee,
+ /*time=*/0, /*entry_height=*/1,
+ /*spends_coinbase=*/true, /*sigops_cost=*/1, lp));
+ m_node.mempool->TrimToSize(0);
+ assert(m_node.mempool->GetMinFee() == target_feerate);
+}
/**
* @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
* with 9 txs.
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 948ebc097c..750f010fb0 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -6,6 +6,7 @@
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <chainparamsbase.h>
+#include <common/args.h>
#include <key.h>
#include <node/caches.h>
#include <node/context.h>
@@ -16,13 +17,13 @@
#include <util/check.h>
#include <util/fs.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/vector.h>
#include <functional>
#include <type_traits>
#include <vector>
+class CFeeRate;
class Chainstate;
/** This is connected to the logger. Can be used to redirect logs to any other log */
@@ -185,6 +186,17 @@ struct TestChain100Setup : public TestingSetup {
*/
std::vector<CTransactionRef> PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit);
+ /** Mock the mempool minimum feerate by adding a transaction and calling TrimToSize(0),
+ * simulating the mempool "reaching capacity" and evicting by descendant feerate. Note that
+ * this clears the mempool, and the new minimum feerate will depend on the maximum feerate of
+ * transactions removed, so this must be called while the mempool is empty.
+ *
+ * @param target_feerate The new mempool minimum feerate after this function returns.
+ * Must be above max(incremental feerate, min relay feerate),
+ * or 1sat/vB with default settings.
+ */
+ void MockMempoolMinFee(const CFeeRate& target_feerate);
+
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 26c48eb0e0..7398091215 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -36,12 +36,12 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
};
- constexpr size_t MAX_COINS_CACHE_BYTES = 1024;
+ // PoolResource defaults to 256 KiB that will be allocated, so we'll take that and make it a bit larger.
+ constexpr size_t MAX_COINS_CACHE_BYTES = 262144 + 512;
// Without any coins in the cache, we shouldn't need to flush.
- BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
- CoinsCacheSizeState::OK);
+ BOOST_TEST(
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0) != CoinsCacheSizeState::CRITICAL);
// If the initial memory allocations of cacheCoins don't match these common
// cases, we can't really continue to make assertions about memory usage.
@@ -71,13 +71,21 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// cacheCoins (unordered_map) preallocates.
constexpr int COINS_UNTIL_CRITICAL{3};
+ // no coin added, so we have plenty of space left.
+ BOOST_CHECK_EQUAL(
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
+ CoinsCacheSizeState::OK);
+
for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
const COutPoint res = AddTestCoin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
+
+ // adding first coin causes the MemoryResource to allocate one 256 KiB chunk of memory,
+ // pushing us immediately over to LARGE
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
- CoinsCacheSizeState::OK);
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0),
+ CoinsCacheSizeState::LARGE);
}
// Adding some additional coins will push us over the edge to CRITICAL.
@@ -94,16 +102,16 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
CoinsCacheSizeState::CRITICAL);
- // Passing non-zero max mempool usage should allow us more headroom.
+ // Passing non-zero max mempool usage (512 KiB) should allow us more headroom.
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
CoinsCacheSizeState::OK);
for (int i{0}; i < 3; ++i) {
AddTestCoin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
CoinsCacheSizeState::OK);
}
@@ -119,7 +127,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
BOOST_CHECK(usage_percentage >= 0.9);
BOOST_CHECK(usage_percentage < 1);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10), // 1024
CoinsCacheSizeState::LARGE);
}
@@ -131,8 +139,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
CoinsCacheSizeState::OK);
}
- // Flushing the view doesn't take us back to OK because cacheCoins has
- // preallocated memory that doesn't get reclaimed even after flush.
+ // Flushing the view does take us back to OK because ReallocateCache() is called
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
@@ -144,7 +151,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
- CoinsCacheSizeState::CRITICAL);
+ CoinsCacheSizeState::OK);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 6ea49cb4ee..0b4c491615 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
const auto params = CreateChainParams(*m_node.args, CBaseChainParams::REGTEST);
// These heights don't have assumeutxo configurations associated, per the contents
- // of chainparams.cpp.
+ // of kernel/chainparams.cpp.
std::vector<int> bad_heights{0, 100, 111, 115, 209, 211};
for (auto empty : bad_heights) {
diff --git a/src/test/validationinterface_tests.cpp b/src/test/validationinterface_tests.cpp
index ceba689e52..576768a625 100644
--- a/src/test/validationinterface_tests.cpp
+++ b/src/test/validationinterface_tests.cpp
@@ -10,6 +10,8 @@
#include <util/check.h>
#include <validationinterface.h>
+#include <atomic>
+
BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, TestingSetup)
struct TestSubscriberNoop final : public CValidationInterface {
diff --git a/src/timedata.cpp b/src/timedata.cpp
index a0646b4707..15ca90ee6a 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -8,11 +8,12 @@
#include <timedata.h>
+#include <common/args.h>
+#include <logging.h>
#include <netaddress.h>
#include <node/interface_ui.h>
#include <sync.h>
#include <tinyformat.h>
-#include <util/system.h>
#include <util/translation.h>
#include <warnings.h>
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index d4daeacd3e..92b55f9fc4 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -7,15 +7,16 @@
#include <chainparams.h>
#include <chainparamsbase.h>
+#include <common/args.h>
#include <compat/compat.h>
#include <crypto/hmac_sha256.h>
+#include <logging.h>
#include <net.h>
#include <netaddress.h>
#include <netbase.h>
#include <util/readwritefile.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
-#include <util/system.h>
#include <util/thread.h>
#include <util/time.h>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 032dfee3ea..da875c271e 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -10,6 +10,7 @@
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
+#include <logging.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 630eff99e0..598e6adb88 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -5,17 +5,10 @@
#include <util/system.h>
-#include <chainparamsbase.h>
-#include <sync.h>
-#include <util/check.h>
-#include <util/fs.h>
-#include <util/fs_helpers.h>
-#include <util/getuniquepath.h>
-#include <util/strencodings.h>
+#include <logging.h>
#include <util/string.h>
#include <util/syserror.h>
-#include <util/translation.h>
-
+#include <util/time.h>
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
#include <pthread.h>
@@ -23,991 +16,25 @@
#endif
#ifndef WIN32
-#include <algorithm>
-#include <cassert>
-#include <fcntl.h>
#include <sched.h>
-#include <sys/resource.h>
#include <sys/stat.h>
-
#else
-
#include <codecvt>
-
-#include <shellapi.h>
-#include <shlobj.h>
#endif
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
-#include <univalue.h>
-
-#include <fstream>
-#include <map>
-#include <memory>
-#include <optional>
+#include <cstdlib>
+#include <locale>
+#include <stdexcept>
#include <string>
-#include <system_error>
#include <thread>
-#include <typeinfo>
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
-const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
-const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
-
-ArgsManager gArgs;
-
-/**
- * Interpret a string argument as a boolean.
- *
- * The definition of LocaleIndependentAtoi<int>() requires that non-numeric string values
- * like "foo", return 0. This means that if a user unintentionally supplies a
- * non-integer argument here, the return value is always false. This means that
- * -foo=false does what the user probably expects, but -foo=true is well defined
- * but does not do what they probably expected.
- *
- * The return value of LocaleIndependentAtoi<int>(...) is zero when given input not
- * representable as an int.
- *
- * For a more extensive discussion of this topic (and a wide range of opinions
- * on the Right Way to change this code), see PR12713.
- */
-static bool InterpretBool(const std::string& strValue)
-{
- if (strValue.empty())
- return true;
- return (LocaleIndependentAtoi<int>(strValue) != 0);
-}
-
-static std::string SettingName(const std::string& arg)
-{
- return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
-}
-
-struct KeyInfo {
- std::string name;
- std::string section;
- bool negated{false};
-};
-
-/**
- * Parse "name", "section.name", "noname", "section.noname" settings keys.
- *
- * @note Where an option was negated can be later checked using the
- * IsArgNegated() method. One use case for this is to have a way to disable
- * options that are not normally boolean (e.g. using -nodebuglogfile to request
- * that debug log output is not sent to any file at all).
- */
-KeyInfo InterpretKey(std::string key)
-{
- KeyInfo result;
- // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
- size_t option_index = key.find('.');
- if (option_index != std::string::npos) {
- result.section = key.substr(0, option_index);
- key.erase(0, option_index + 1);
- }
- if (key.substr(0, 2) == "no") {
- key.erase(0, 2);
- result.negated = true;
- }
- result.name = key;
- return result;
-}
-
-/**
- * Interpret settings value based on registered flags.
- *
- * @param[in] key key information to know if key was negated
- * @param[in] value string value of setting to be parsed
- * @param[in] flags ArgsManager registered argument flags
- * @param[out] error Error description if settings value is not valid
- *
- * @return parsed settings value if it is valid, otherwise nullopt accompanied
- * by a descriptive error string
- */
-static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
- unsigned int flags, std::string& error)
-{
- // Return negated settings as false values.
- if (key.negated) {
- if (flags & ArgsManager::DISALLOW_NEGATION) {
- error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
- return std::nullopt;
- }
- // Double negatives like -nofoo=0 are supported (but discouraged)
- if (value && !InterpretBool(*value)) {
- LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
- return true;
- }
- return false;
- }
- if (!value && (flags & ArgsManager::DISALLOW_ELISION)) {
- error = strprintf("Can not set -%s with no value. Please specify value with -%s=value.", key.name, key.name);
- return std::nullopt;
- }
- return value ? *value : "";
-}
-
-// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
-// #include class definitions for all members.
-// For example, m_settings has an internal dependency on univalue.
-ArgsManager::ArgsManager() = default;
-ArgsManager::~ArgsManager() = default;
-
-std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
-{
- std::set<std::string> unsuitables;
-
- LOCK(cs_args);
-
- // if there's no section selected, don't worry
- if (m_network.empty()) return std::set<std::string> {};
-
- // if it's okay to use the default section for this network, don't worry
- if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
-
- for (const auto& arg : m_network_only_args) {
- if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
- unsuitables.insert(arg);
- }
- }
- return unsuitables;
-}
-
-std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
-{
- // Section names to be recognized in the config file.
- static const std::set<std::string> available_sections{
- CBaseChainParams::REGTEST,
- CBaseChainParams::SIGNET,
- CBaseChainParams::TESTNET,
- CBaseChainParams::MAIN
- };
-
- LOCK(cs_args);
- std::list<SectionInfo> unrecognized = m_config_sections;
- unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
- return unrecognized;
-}
-
-void ArgsManager::SelectConfigNetwork(const std::string& network)
-{
- LOCK(cs_args);
- m_network = network;
-}
-
-bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
-{
- LOCK(cs_args);
- m_settings.command_line_options.clear();
-
- for (int i = 1; i < argc; i++) {
- std::string key(argv[i]);
-
-#ifdef MAC_OSX
- // At the first time when a user gets the "App downloaded from the
- // internet" warning, and clicks the Open button, macOS passes
- // a unique process serial number (PSN) as -psn_... command-line
- // argument, which we filter out.
- if (key.substr(0, 5) == "-psn_") continue;
-#endif
-
- if (key == "-") break; //bitcoin-tx using stdin
- std::optional<std::string> val;
- size_t is_index = key.find('=');
- if (is_index != std::string::npos) {
- val = key.substr(is_index + 1);
- key.erase(is_index);
- }
-#ifdef WIN32
- key = ToLower(key);
- if (key[0] == '/')
- key[0] = '-';
-#endif
-
- if (key[0] != '-') {
- if (!m_accept_any_command && m_command.empty()) {
- // The first non-dash arg is a registered command
- std::optional<unsigned int> flags = GetArgFlags(key);
- if (!flags || !(*flags & ArgsManager::COMMAND)) {
- error = strprintf("Invalid command '%s'", argv[i]);
- return false;
- }
- }
- m_command.push_back(key);
- while (++i < argc) {
- // The remaining args are command args
- m_command.push_back(argv[i]);
- }
- break;
- }
-
- // Transform --foo to -foo
- if (key.length() > 1 && key[1] == '-')
- key.erase(0, 1);
-
- // Transform -foo to foo
- key.erase(0, 1);
- KeyInfo keyinfo = InterpretKey(key);
- std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
-
- // Unknown command line options and command line options with dot
- // characters (which are returned from InterpretKey with nonempty
- // section strings) are not valid.
- if (!flags || !keyinfo.section.empty()) {
- error = strprintf("Invalid parameter %s", argv[i]);
- return false;
- }
-
- std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
- if (!value) return false;
-
- m_settings.command_line_options[keyinfo.name].push_back(*value);
- }
-
- // we do not allow -includeconf from command line, only -noincludeconf
- if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
- const util::SettingsSpan values{*includes};
- // Range may be empty if -noincludeconf was passed
- if (!values.empty()) {
- error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
- return false; // pick first value as example
- }
- }
- return true;
-}
-
-std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
-{
- LOCK(cs_args);
- for (const auto& arg_map : m_available_args) {
- const auto search = arg_map.second.find(name);
- if (search != arg_map.second.end()) {
- return search->second.m_flags;
- }
- }
- return std::nullopt;
-}
-
-fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
-{
- if (IsArgNegated(arg)) return fs::path{};
- std::string path_str = GetArg(arg, "");
- if (path_str.empty()) return default_value;
- fs::path result = fs::PathFromString(path_str).lexically_normal();
- // Remove trailing slash, if present.
- return result.has_filename() ? result : result.parent_path();
-}
-
-const fs::path& ArgsManager::GetBlocksDirPath() const
-{
- LOCK(cs_args);
- fs::path& path = m_cached_blocks_path;
-
- // Cache the path to avoid calling fs::create_directories on every call of
- // this function
- if (!path.empty()) return path;
-
- if (IsArgSet("-blocksdir")) {
- path = fs::absolute(GetPathArg("-blocksdir"));
- if (!fs::is_directory(path)) {
- path = "";
- return path;
- }
- } else {
- path = GetDataDirBase();
- }
-
- path /= fs::PathFromString(BaseParams().DataDir());
- path /= "blocks";
- fs::create_directories(path);
- return path;
-}
-
-const fs::path& ArgsManager::GetDataDir(bool net_specific) const
-{
- LOCK(cs_args);
- fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
-
- // Used cached path if available
- if (!path.empty()) return path;
-
- const fs::path datadir{GetPathArg("-datadir")};
- if (!datadir.empty()) {
- path = fs::absolute(datadir);
- if (!fs::is_directory(path)) {
- path = "";
- return path;
- }
- } else {
- path = GetDefaultDataDir();
- }
-
- if (net_specific && !BaseParams().DataDir().empty()) {
- path /= fs::PathFromString(BaseParams().DataDir());
- }
-
- return path;
-}
-
-void ArgsManager::ClearPathCache()
-{
- LOCK(cs_args);
-
- m_cached_datadir_path = fs::path();
- m_cached_network_datadir_path = fs::path();
- m_cached_blocks_path = fs::path();
-}
-
-std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
-{
- Command ret;
- LOCK(cs_args);
- auto it = m_command.begin();
- if (it == m_command.end()) {
- // No command was passed
- return std::nullopt;
- }
- if (!m_accept_any_command) {
- // The registered command
- ret.command = *(it++);
- }
- while (it != m_command.end()) {
- // The unregistered command and args (if any)
- ret.args.push_back(*(it++));
- }
- return ret;
-}
-
-std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
-{
- std::vector<std::string> result;
- for (const util::SettingsValue& value : GetSettingsList(strArg)) {
- result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
- }
- return result;
-}
-
-bool ArgsManager::IsArgSet(const std::string& strArg) const
-{
- return !GetSetting(strArg).isNull();
-}
-
-bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
-{
- fs::path settings = GetPathArg("-settings", BITCOIN_SETTINGS_FILENAME);
- if (settings.empty()) {
- return false;
- }
- if (backup) {
- settings += ".bak";
- }
- if (filepath) {
- *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
- }
- return true;
-}
-
-static void SaveErrors(const std::vector<std::string> errors, std::vector<std::string>* error_out)
-{
- for (const auto& error : errors) {
- if (error_out) {
- error_out->emplace_back(error);
- } else {
- LogPrintf("%s\n", error);
- }
- }
-}
-
-bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
-{
- fs::path path;
- if (!GetSettingsPath(&path, /* temp= */ false)) {
- return true; // Do nothing if settings file disabled.
- }
-
- LOCK(cs_args);
- m_settings.rw_settings.clear();
- std::vector<std::string> read_errors;
- if (!util::ReadSettings(path, m_settings.rw_settings, read_errors)) {
- SaveErrors(read_errors, errors);
- return false;
- }
- for (const auto& setting : m_settings.rw_settings) {
- KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
- if (!GetArgFlags('-' + key.name)) {
- LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
- }
- }
- return true;
-}
-
-bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
-{
- fs::path path, path_tmp;
- if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
- throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
- }
-
- LOCK(cs_args);
- std::vector<std::string> write_errors;
- if (!util::WriteSettings(path_tmp, m_settings.rw_settings, write_errors)) {
- SaveErrors(write_errors, errors);
- return false;
- }
- if (!RenameOver(path_tmp, path)) {
- SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
- return false;
- }
- return true;
-}
-
-util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
-{
- LOCK(cs_args);
- return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
- /*ignore_nonpersistent=*/true, /*get_chain_name=*/false);
-}
-
-bool ArgsManager::IsArgNegated(const std::string& strArg) const
-{
- return GetSetting(strArg).isFalse();
-}
-
-std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
-{
- return GetArg(strArg).value_or(strDefault);
-}
-
-std::optional<std::string> ArgsManager::GetArg(const std::string& strArg) const
-{
- const util::SettingsValue value = GetSetting(strArg);
- return SettingToString(value);
-}
-
-std::optional<std::string> SettingToString(const util::SettingsValue& value)
-{
- if (value.isNull()) return std::nullopt;
- if (value.isFalse()) return "0";
- if (value.isTrue()) return "1";
- if (value.isNum()) return value.getValStr();
- return value.get_str();
-}
-
-std::string SettingToString(const util::SettingsValue& value, const std::string& strDefault)
-{
- return SettingToString(value).value_or(strDefault);
-}
-
-int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
-{
- return GetIntArg(strArg).value_or(nDefault);
-}
-
-std::optional<int64_t> ArgsManager::GetIntArg(const std::string& strArg) const
-{
- const util::SettingsValue value = GetSetting(strArg);
- return SettingToInt(value);
-}
-
-std::optional<int64_t> SettingToInt(const util::SettingsValue& value)
-{
- if (value.isNull()) return std::nullopt;
- if (value.isFalse()) return 0;
- if (value.isTrue()) return 1;
- if (value.isNum()) return value.getInt<int64_t>();
- return LocaleIndependentAtoi<int64_t>(value.get_str());
-}
-
-int64_t SettingToInt(const util::SettingsValue& value, int64_t nDefault)
-{
- return SettingToInt(value).value_or(nDefault);
-}
-
-bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
-{
- return GetBoolArg(strArg).value_or(fDefault);
-}
-
-std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
-{
- const util::SettingsValue value = GetSetting(strArg);
- return SettingToBool(value);
-}
-
-std::optional<bool> SettingToBool(const util::SettingsValue& value)
-{
- if (value.isNull()) return std::nullopt;
- if (value.isBool()) return value.get_bool();
- return InterpretBool(value.get_str());
-}
-
-bool SettingToBool(const util::SettingsValue& value, bool fDefault)
-{
- return SettingToBool(value).value_or(fDefault);
-}
-
-bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
-{
- LOCK(cs_args);
- if (IsArgSet(strArg)) return false;
- ForceSetArg(strArg, strValue);
- return true;
-}
-
-bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
-{
- if (fValue)
- return SoftSetArg(strArg, std::string("1"));
- else
- return SoftSetArg(strArg, std::string("0"));
-}
-
-void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
-{
- LOCK(cs_args);
- m_settings.forced_settings[SettingName(strArg)] = strValue;
-}
-
-void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
-{
- Assert(cmd.find('=') == std::string::npos);
- Assert(cmd.at(0) != '-');
-
- LOCK(cs_args);
- m_accept_any_command = false; // latch to false
- std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
- auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
- Assert(ret.second); // Fail on duplicate commands
-}
-
-void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
-{
- Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
-
- // Split arg name from its help param
- size_t eq_index = name.find('=');
- if (eq_index == std::string::npos) {
- eq_index = name.size();
- }
- std::string arg_name = name.substr(0, eq_index);
-
- LOCK(cs_args);
- std::map<std::string, Arg>& arg_map = m_available_args[cat];
- auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
- assert(ret.second); // Make sure an insertion actually happened
-
- if (flags & ArgsManager::NETWORK_ONLY) {
- m_network_only_args.emplace(arg_name);
- }
-}
-
-void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
-{
- for (const std::string& name : names) {
- AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
- }
-}
-
-std::string ArgsManager::GetHelpMessage() const
-{
- const bool show_debug = GetBoolArg("-help-debug", false);
-
- std::string usage;
- LOCK(cs_args);
- for (const auto& arg_map : m_available_args) {
- switch(arg_map.first) {
- case OptionsCategory::OPTIONS:
- usage += HelpMessageGroup("Options:");
- break;
- case OptionsCategory::CONNECTION:
- usage += HelpMessageGroup("Connection options:");
- break;
- case OptionsCategory::ZMQ:
- usage += HelpMessageGroup("ZeroMQ notification options:");
- break;
- case OptionsCategory::DEBUG_TEST:
- usage += HelpMessageGroup("Debugging/Testing options:");
- break;
- case OptionsCategory::NODE_RELAY:
- usage += HelpMessageGroup("Node relay options:");
- break;
- case OptionsCategory::BLOCK_CREATION:
- usage += HelpMessageGroup("Block creation options:");
- break;
- case OptionsCategory::RPC:
- usage += HelpMessageGroup("RPC server options:");
- break;
- case OptionsCategory::WALLET:
- usage += HelpMessageGroup("Wallet options:");
- break;
- case OptionsCategory::WALLET_DEBUG_TEST:
- if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
- break;
- case OptionsCategory::CHAINPARAMS:
- usage += HelpMessageGroup("Chain selection options:");
- break;
- case OptionsCategory::GUI:
- usage += HelpMessageGroup("UI Options:");
- break;
- case OptionsCategory::COMMANDS:
- usage += HelpMessageGroup("Commands:");
- break;
- case OptionsCategory::REGISTER_COMMANDS:
- usage += HelpMessageGroup("Register Commands:");
- break;
- default:
- break;
- }
-
- // When we get to the hidden options, stop
- if (arg_map.first == OptionsCategory::HIDDEN) break;
-
- for (const auto& arg : arg_map.second) {
- if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
- std::string name;
- if (arg.second.m_help_param.empty()) {
- name = arg.first;
- } else {
- name = arg.first + arg.second.m_help_param;
- }
- usage += HelpMessageOpt(name, arg.second.m_help_text);
- }
- }
- }
- return usage;
-}
-
-bool HelpRequested(const ArgsManager& args)
-{
- return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
-}
-
-void SetupHelpOptions(ArgsManager& args)
-{
- args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- args.AddHiddenArgs({"-h", "-help"});
-}
-
-static const int screenWidth = 79;
-static const int optIndent = 2;
-static const int msgIndent = 7;
-
-std::string HelpMessageGroup(const std::string &message) {
- return std::string(message) + std::string("\n\n");
-}
-
-std::string HelpMessageOpt(const std::string &option, const std::string &message) {
- return std::string(optIndent,' ') + std::string(option) +
- std::string("\n") + std::string(msgIndent,' ') +
- FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
- std::string("\n\n");
-}
-
-fs::path GetDefaultDataDir()
-{
- // Windows: C:\Users\Username\AppData\Roaming\Bitcoin
- // macOS: ~/Library/Application Support/Bitcoin
- // Unix-like: ~/.bitcoin
-#ifdef WIN32
- // Windows
- return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
-#else
- fs::path pathRet;
- char* pszHome = getenv("HOME");
- if (pszHome == nullptr || strlen(pszHome) == 0)
- pathRet = fs::path("/");
- else
- pathRet = fs::path(pszHome);
-#ifdef MAC_OSX
- // macOS
- return pathRet / "Library/Application Support/Bitcoin";
-#else
- // Unix-like
- return pathRet / ".bitcoin";
-#endif
-#endif
-}
-
-bool CheckDataDirOption(const ArgsManager& args)
-{
- const fs::path datadir{args.GetPathArg("-datadir")};
- return datadir.empty() || fs::is_directory(fs::absolute(datadir));
-}
-
-fs::path GetConfigFile(const ArgsManager& args, const fs::path& configuration_file_path)
-{
- return AbsPathForConfigVal(args, configuration_file_path, /*net_specific=*/false);
-}
-
-static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
-{
- std::string str, prefix;
- std::string::size_type pos;
- int linenr = 1;
- while (std::getline(stream, str)) {
- bool used_hash = false;
- if ((pos = str.find('#')) != std::string::npos) {
- str = str.substr(0, pos);
- used_hash = true;
- }
- const static std::string pattern = " \t\r\n";
- str = TrimString(str, pattern);
- if (!str.empty()) {
- if (*str.begin() == '[' && *str.rbegin() == ']') {
- const std::string section = str.substr(1, str.size() - 2);
- sections.emplace_back(SectionInfo{section, filepath, linenr});
- prefix = section + '.';
- } else if (*str.begin() == '-') {
- error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
- return false;
- } else if ((pos = str.find('=')) != std::string::npos) {
- std::string name = prefix + TrimString(std::string_view{str}.substr(0, pos), pattern);
- std::string_view value = TrimStringView(std::string_view{str}.substr(pos + 1), pattern);
- if (used_hash && name.find("rpcpassword") != std::string::npos) {
- error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
- return false;
- }
- options.emplace_back(name, value);
- if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
- sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
- }
- } else {
- error = strprintf("parse error on line %i: %s", linenr, str);
- if (str.size() >= 2 && str.substr(0, 2) == "no") {
- error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
- }
- return false;
- }
- }
- ++linenr;
- }
- return true;
-}
-
-bool IsConfSupported(KeyInfo& key, std::string& error) {
- if (key.name == "conf") {
- error = "conf cannot be set in the configuration file; use includeconf= if you want to include additional config files";
- return false;
- }
- if (key.name == "reindex") {
- // reindex can be set in a config file but it is strongly discouraged as this will cause the node to reindex on
- // every restart. Allow the config but throw a warning
- LogPrintf("Warning: reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary\n");
- return true;
- }
- return true;
-}
-
-bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
-{
- LOCK(cs_args);
- std::vector<std::pair<std::string, std::string>> options;
- if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
- return false;
- }
- for (const std::pair<std::string, std::string>& option : options) {
- KeyInfo key = InterpretKey(option.first);
- std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
- if (!IsConfSupported(key, error)) return false;
- if (flags) {
- std::optional<util::SettingsValue> value = InterpretValue(key, &option.second, *flags, error);
- if (!value) {
- return false;
- }
- m_settings.ro_config[key.section][key.name].push_back(*value);
- } else {
- if (ignore_invalid_keys) {
- LogPrintf("Ignoring unknown configuration value %s\n", option.first);
- } else {
- error = strprintf("Invalid configuration value %s", option.first);
- return false;
- }
- }
- }
- return true;
-}
-
-fs::path ArgsManager::GetConfigFilePath() const
-{
- return GetConfigFile(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME));
-}
-
-bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
-{
- {
- LOCK(cs_args);
- m_settings.ro_config.clear();
- m_config_sections.clear();
- }
-
- const auto conf_path{GetConfigFilePath()};
- std::ifstream stream{conf_path};
-
- // not ok to have a config file specified that cannot be opened
- if (IsArgSet("-conf") && !stream.good()) {
- error = strprintf("specified config file \"%s\" could not be opened.", fs::PathToString(conf_path));
- return false;
- }
- // ok to not have a config file
- if (stream.good()) {
- if (!ReadConfigStream(stream, fs::PathToString(conf_path), error, ignore_invalid_keys)) {
- return false;
- }
- // `-includeconf` cannot be included in the command line arguments except
- // as `-noincludeconf` (which indicates that no included conf file should be used).
- bool use_conf_file{true};
- {
- LOCK(cs_args);
- if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
- // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
- assert(util::SettingsSpan(*includes).last_negated());
- use_conf_file = false;
- }
- }
- if (use_conf_file) {
- std::string chain_id = GetChainName();
- std::vector<std::string> conf_file_names;
-
- auto add_includes = [&](const std::string& network, size_t skip = 0) {
- size_t num_values = 0;
- LOCK(cs_args);
- if (auto* section = util::FindKey(m_settings.ro_config, network)) {
- if (auto* values = util::FindKey(*section, "includeconf")) {
- for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
- conf_file_names.push_back((*values)[i].get_str());
- }
- num_values = values->size();
- }
- }
- return num_values;
- };
-
- // We haven't set m_network yet (that happens in SelectParams()), so manually check
- // for network.includeconf args.
- const size_t chain_includes = add_includes(chain_id);
- const size_t default_includes = add_includes({});
-
- for (const std::string& conf_file_name : conf_file_names) {
- std::ifstream conf_file_stream{GetConfigFile(*this, fs::PathFromString(conf_file_name))};
- if (conf_file_stream.good()) {
- if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
- return false;
- }
- LogPrintf("Included configuration file %s\n", conf_file_name);
- } else {
- error = "Failed to include configuration file " + conf_file_name;
- return false;
- }
- }
-
- // Warn about recursive -includeconf
- conf_file_names.clear();
- add_includes(chain_id, /* skip= */ chain_includes);
- add_includes({}, /* skip= */ default_includes);
- std::string chain_id_final = GetChainName();
- if (chain_id_final != chain_id) {
- // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
- add_includes(chain_id_final);
- }
- for (const std::string& conf_file_name : conf_file_names) {
- tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
- }
- }
- }
-
- // If datadir is changed in .conf file:
- ClearPathCache();
- if (!CheckDataDirOption(*this)) {
- error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
- return false;
- }
- return true;
-}
-
-std::string ArgsManager::GetChainName() const
-{
- auto get_net = [&](const std::string& arg) {
- LOCK(cs_args);
- util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
- /* ignore_default_section_config= */ false,
- /*ignore_nonpersistent=*/false,
- /* get_chain_name= */ true);
- return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
- };
-
- const bool fRegTest = get_net("-regtest");
- const bool fSigNet = get_net("-signet");
- const bool fTestNet = get_net("-testnet");
- const bool is_chain_arg_set = IsArgSet("-chain");
-
- if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) {
- throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one.");
- }
- if (fRegTest)
- return CBaseChainParams::REGTEST;
- if (fSigNet) {
- return CBaseChainParams::SIGNET;
- }
- if (fTestNet)
- return CBaseChainParams::TESTNET;
-
- return GetArg("-chain", CBaseChainParams::MAIN);
-}
-
-bool ArgsManager::UseDefaultSection(const std::string& arg) const
-{
- return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0;
-}
-
-util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
-{
- LOCK(cs_args);
- return util::GetSetting(
- m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
- /*ignore_nonpersistent=*/false, /*get_chain_name=*/false);
-}
-
-std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
-{
- LOCK(cs_args);
- return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
-}
-
-void ArgsManager::logArgsPrefix(
- const std::string& prefix,
- const std::string& section,
- const std::map<std::string, std::vector<util::SettingsValue>>& args) const
-{
- std::string section_str = section.empty() ? "" : "[" + section + "] ";
- for (const auto& arg : args) {
- for (const auto& value : arg.second) {
- std::optional<unsigned int> flags = GetArgFlags('-' + arg.first);
- if (flags) {
- std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
- LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
- }
- }
- }
-}
-
-void ArgsManager::LogArgs() const
-{
- LOCK(cs_args);
- for (const auto& section : m_settings.ro_config) {
- logArgsPrefix("Config file arg:", section.first, section.second);
- }
- for (const auto& setting : m_settings.rw_settings) {
- LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
- }
- logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
-}
-
#ifndef WIN32
std::string ShellEscape(const std::string& arg)
{
@@ -1086,14 +113,6 @@ int64_t GetStartupTime()
return nStartupTime;
}
-fs::path AbsPathForConfigVal(const ArgsManager& args, const fs::path& path, bool net_specific)
-{
- if (path.is_absolute()) {
- return path;
- }
- return fsbridge::AbsPathJoin(net_specific ? args.GetDataDirNet() : args.GetDataDirBase(), path);
-}
-
void ScheduleBatchPriority()
{
#ifdef SCHED_BATCH
@@ -1104,30 +123,3 @@ void ScheduleBatchPriority()
}
#endif
}
-
-namespace util {
-#ifdef WIN32
-WinCmdLineArgs::WinCmdLineArgs()
-{
- wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
- std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
- argv = new char*[argc];
- args.resize(argc);
- for (int i = 0; i < argc; i++) {
- args[i] = utf8_cvt.to_bytes(wargv[i]);
- argv[i] = &*args[i].begin();
- }
- LocalFree(wargv);
-}
-
-WinCmdLineArgs::~WinCmdLineArgs()
-{
- delete[] argv;
-}
-
-std::pair<int, char**> WinCmdLineArgs::get()
-{
- return std::make_pair(argc, argv);
-}
-#endif
-} // namespace util
diff --git a/src/util/system.h b/src/util/system.h
index ff3155b498..e2fc3450f6 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -3,10 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-/**
- * Server/client environment: argument handling, config file parsing,
- * thread wrappers, startup time
- */
#ifndef BITCOIN_UTIL_SYSTEM_H
#define BITCOIN_UTIL_SYSTEM_H
@@ -16,35 +12,17 @@
#include <compat/assumptions.h>
#include <compat/compat.h>
-#include <logging.h>
-#include <sync.h>
-#include <util/fs.h>
-#include <util/settings.h>
-#include <util/time.h>
#include <any>
-#include <map>
-#include <optional>
#include <set>
#include <stdint.h>
#include <string>
-#include <utility>
-#include <vector>
-
-class ArgsManager;
-class UniValue;
// Application startup time (used for uptime calculation)
int64_t GetStartupTime();
-extern const char * const BITCOIN_CONF_FILENAME;
-extern const char * const BITCOIN_SETTINGS_FILENAME;
-
void SetupEnvironment();
bool SetupNetworking();
-// Return true if -datadir option points to a valid directory or is not specified.
-bool CheckDataDirOption(const ArgsManager& args);
-fs::path GetConfigFile(const ArgsManager& args, const fs::path& configuration_file_path);
#ifndef WIN32
std::string ShellEscape(const std::string& arg);
#endif
@@ -53,413 +31,6 @@ void runCommand(const std::string& strCommand);
#endif
/**
- * Most paths passed as configuration arguments are treated as relative to
- * the datadir if they are not absolute.
- *
- * @param args Parsed arguments and settings.
- * @param path The path to be conditionally prefixed with datadir.
- * @param net_specific Use network specific datadir variant
- * @return The normalized path.
- */
-fs::path AbsPathForConfigVal(const ArgsManager& args, const fs::path& path, bool net_specific = true);
-
-inline bool IsSwitchChar(char c)
-{
-#ifdef WIN32
- return c == '-' || c == '/';
-#else
- return c == '-';
-#endif
-}
-
-enum class OptionsCategory {
- OPTIONS,
- CONNECTION,
- WALLET,
- WALLET_DEBUG_TEST,
- ZMQ,
- DEBUG_TEST,
- CHAINPARAMS,
- NODE_RELAY,
- BLOCK_CREATION,
- RPC,
- GUI,
- COMMANDS,
- REGISTER_COMMANDS,
-
- HIDDEN // Always the last option to avoid printing these in the help
-};
-
-struct SectionInfo
-{
- std::string m_name;
- std::string m_file;
- int m_line;
-};
-
-std::string SettingToString(const util::SettingsValue&, const std::string&);
-std::optional<std::string> SettingToString(const util::SettingsValue&);
-
-int64_t SettingToInt(const util::SettingsValue&, int64_t);
-std::optional<int64_t> SettingToInt(const util::SettingsValue&);
-
-bool SettingToBool(const util::SettingsValue&, bool);
-std::optional<bool> SettingToBool(const util::SettingsValue&);
-
-class ArgsManager
-{
-public:
- /**
- * Flags controlling how config and command line arguments are validated and
- * interpreted.
- */
- enum Flags : uint32_t {
- ALLOW_ANY = 0x01, //!< disable validation
- // ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
- // ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
- // ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
- // ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
- DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
- DISALLOW_ELISION = 0x40, //!< disallow -foo syntax that doesn't assign any value
-
- DEBUG_ONLY = 0x100,
- /* Some options would cause cross-contamination if values for
- * mainnet were used while running on regtest/testnet (or vice-versa).
- * Setting them as NETWORK_ONLY ensures that sharing a config file
- * between mainnet and regtest/testnet won't cause problems due to these
- * parameters by accident. */
- NETWORK_ONLY = 0x200,
- // This argument's value is sensitive (such as a password).
- SENSITIVE = 0x400,
- COMMAND = 0x800,
- };
-
-protected:
- struct Arg
- {
- std::string m_help_param;
- std::string m_help_text;
- unsigned int m_flags;
- };
-
- mutable RecursiveMutex cs_args;
- util::Settings m_settings GUARDED_BY(cs_args);
- std::vector<std::string> m_command GUARDED_BY(cs_args);
- std::string m_network GUARDED_BY(cs_args);
- std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
- std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
- bool m_accept_any_command GUARDED_BY(cs_args){true};
- std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
- mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
- mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
- mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
-
- [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
-
- /**
- * Returns true if settings values from the default section should be used,
- * depending on the current network and whether the setting is
- * network-specific.
- */
- bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
-
- public:
- /**
- * Get setting value.
- *
- * Result will be null if setting was unset, true if "-setting" argument was passed
- * false if "-nosetting" argument was passed, and a string if a "-setting=value"
- * argument was passed.
- */
- util::SettingsValue GetSetting(const std::string& arg) const;
-
- /**
- * Get list of setting values.
- */
- std::vector<util::SettingsValue> GetSettingsList(const std::string& arg) const;
-
- ArgsManager();
- ~ArgsManager();
-
- /**
- * Select the network in use
- */
- void SelectConfigNetwork(const std::string& network);
-
- [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
-
- /**
- * Return config file path (read-only)
- */
- fs::path GetConfigFilePath() const;
- [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
-
- /**
- * Log warnings for options in m_section_only_args when
- * they are specified in the default section but not overridden
- * on the command line or in a network-specific section in the
- * config file.
- */
- std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
-
- /**
- * Log warnings for unrecognized section names in the config file.
- */
- std::list<SectionInfo> GetUnrecognizedSections() const;
-
- struct Command {
- /** The command (if one has been registered with AddCommand), or empty */
- std::string command;
- /**
- * If command is non-empty: Any args that followed it
- * If command is empty: The unregistered command and any args that followed it
- */
- std::vector<std::string> args;
- };
- /**
- * Get the command and command args (returns std::nullopt if no command provided)
- */
- std::optional<const Command> GetCommand() const;
-
- /**
- * Get blocks directory path
- *
- * @return Blocks path which is network specific
- */
- const fs::path& GetBlocksDirPath() const;
-
- /**
- * Get data directory path
- *
- * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
- */
- const fs::path& GetDataDirBase() const { return GetDataDir(false); }
-
- /**
- * Get data directory path with appended network identifier
- *
- * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
- */
- const fs::path& GetDataDirNet() const { return GetDataDir(true); }
-
- /**
- * Clear cached directory paths
- */
- void ClearPathCache();
-
- /**
- * Return a vector of strings of the given argument
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @return command-line arguments
- */
- std::vector<std::string> GetArgs(const std::string& strArg) const;
-
- /**
- * Return true if the given argument has been manually set
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @return true if the argument has been set
- */
- bool IsArgSet(const std::string& strArg) const;
-
- /**
- * Return true if the argument was originally passed as a negated option,
- * i.e. -nofoo.
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @return true if the argument was passed negated
- */
- bool IsArgNegated(const std::string& strArg) const;
-
- /**
- * Return string argument or default value
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @param strDefault (e.g. "1")
- * @return command-line argument or default value
- */
- std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
- std::optional<std::string> GetArg(const std::string& strArg) const;
-
- /**
- * Return path argument or default value
- *
- * @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
- * @param default_value Optional default value to return instead of the empty path.
- * @return normalized path if argument is set, with redundant "." and ".."
- * path components and trailing separators removed (see patharg unit test
- * for examples or implementation for details). If argument is empty or not
- * set, default_value is returned unchanged.
- */
- fs::path GetPathArg(std::string arg, const fs::path& default_value = {}) const;
-
- /**
- * Return integer argument or default value
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @param nDefault (e.g. 1)
- * @return command-line argument (0 if invalid number) or default value
- */
- int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
- std::optional<int64_t> GetIntArg(const std::string& strArg) const;
-
- /**
- * Return boolean argument or default value
- *
- * @param strArg Argument to get (e.g. "-foo")
- * @param fDefault (true or false)
- * @return command-line argument or default value
- */
- bool GetBoolArg(const std::string& strArg, bool fDefault) const;
- std::optional<bool> GetBoolArg(const std::string& strArg) const;
-
- /**
- * Set an argument if it doesn't already have a value
- *
- * @param strArg Argument to set (e.g. "-foo")
- * @param strValue Value (e.g. "1")
- * @return true if argument gets set, false if it already had a value
- */
- bool SoftSetArg(const std::string& strArg, const std::string& strValue);
-
- /**
- * Set a boolean argument if it doesn't already have a value
- *
- * @param strArg Argument to set (e.g. "-foo")
- * @param fValue Value (e.g. false)
- * @return true if argument gets set, false if it already had a value
- */
- bool SoftSetBoolArg(const std::string& strArg, bool fValue);
-
- // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
- // been set. Also called directly in testing.
- void ForceSetArg(const std::string& strArg, const std::string& strValue);
-
- /**
- * Returns the appropriate chain name from the program arguments.
- * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given.
- */
- std::string GetChainName() const;
-
- /**
- * Add argument
- */
- void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
-
- /**
- * Add subcommand
- */
- void AddCommand(const std::string& cmd, const std::string& help);
-
- /**
- * Add many hidden arguments
- */
- void AddHiddenArgs(const std::vector<std::string>& args);
-
- /**
- * Clear available arguments
- */
- void ClearArgs() {
- LOCK(cs_args);
- m_available_args.clear();
- m_network_only_args.clear();
- }
-
- /**
- * Get the help string
- */
- std::string GetHelpMessage() const;
-
- /**
- * Return Flags for known arg.
- * Return nullopt for unknown arg.
- */
- std::optional<unsigned int> GetArgFlags(const std::string& name) const;
-
- /**
- * Get settings file path, or return false if read-write settings were
- * disabled with -nosettings.
- */
- bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const;
-
- /**
- * Read settings file. Push errors to vector, or log them if null.
- */
- bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
-
- /**
- * Write settings file or backup settings file. Push errors to vector, or
- * log them if null.
- */
- bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const;
-
- /**
- * Get current setting from config file or read/write settings file,
- * ignoring nonpersistent command line or forced settings values.
- */
- util::SettingsValue GetPersistentSetting(const std::string& name) const;
-
- /**
- * Access settings with lock held.
- */
- template <typename Fn>
- void LockSettings(Fn&& fn)
- {
- LOCK(cs_args);
- fn(m_settings);
- }
-
- /**
- * Log the config file options and the command line arguments,
- * useful for troubleshooting.
- */
- void LogArgs() const;
-
-private:
- /**
- * Get data directory path
- *
- * @param net_specific Append network identifier to the returned path
- * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
- */
- const fs::path& GetDataDir(bool net_specific) const;
-
- // Helper function for LogArgs().
- void logArgsPrefix(
- const std::string& prefix,
- const std::string& section,
- const std::map<std::string, std::vector<util::SettingsValue>>& args) const;
-};
-
-extern ArgsManager gArgs;
-
-/**
- * @return true if help has been requested via a command-line arg
- */
-bool HelpRequested(const ArgsManager& args);
-
-/** Add help options to the args manager */
-void SetupHelpOptions(ArgsManager& args);
-
-/**
- * Format a string to be used as group of options in help messages
- *
- * @param message Group name (e.g. "RPC server options:")
- * @return the formatted string
- */
-std::string HelpMessageGroup(const std::string& message);
-
-/**
- * Format a string to be used as option description in help messages
- *
- * @param option Option message (e.g. "-rpcuser=<user>")
- * @param message Option description (e.g. "Username for JSON-RPC connections")
- * @return the formatted string
- */
-std::string HelpMessageOpt(const std::string& option, const std::string& message);
-
-/**
* Return the number of cores available on the current system.
* @note This does count virtual cores, such as those provided by HyperThreading.
*/
@@ -496,21 +67,6 @@ T* AnyPtr(const std::any& any) noexcept
return ptr ? *ptr : nullptr;
}
-#ifdef WIN32
-class WinCmdLineArgs
-{
-public:
- WinCmdLineArgs();
- ~WinCmdLineArgs();
- std::pair<int, char**> get();
-
-private:
- int argc;
- char** argv;
- std::vector<std::string> args;
-};
-#endif
-
} // namespace util
#endif // BITCOIN_UTIL_SYSTEM_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 1fd8f0e326..b2f4283e16 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -11,6 +11,7 @@
#include <arith_uint256.h>
#include <chain.h>
#include <checkqueue.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
@@ -845,9 +846,18 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
strprintf("%d", nSigOpsCost));
- // No individual transactions are allowed below the min relay feerate and mempool min feerate except from
- // disconnected blocks and transactions in a package. Package transactions will be checked using
- // package feerate later.
+ // No individual transactions are allowed below the min relay feerate except from disconnected blocks.
+ // This requirement, unlike CheckFeeRate, cannot be bypassed using m_package_feerates because,
+ // while a tx could be package CPFP'd when entering the mempool, we do not have a DoS-resistant
+ // method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
+ // due to a replacement.
+ if (!bypass_limits && ws.m_modified_fees < m_pool.m_min_relay_feerate.GetFee(ws.m_vsize)) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
+ strprintf("%d < %d", ws.m_modified_fees, m_pool.m_min_relay_feerate.GetFee(ws.m_vsize)));
+ }
+ // No individual transactions are allowed below the mempool min feerate except from disconnected
+ // blocks and transactions in a package. Package transactions will be checked using package
+ // feerate later.
if (!bypass_limits && !args.m_package_feerates && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
@@ -1191,6 +1201,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
} else {
all_submitted = false;
ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
}
}
@@ -4931,7 +4942,6 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
} else {
// Otherwise, flush state to disk and deallocate the in-memory coins map.
ret = FlushStateToDisk(state, FlushStateMode::ALWAYS);
- CoinsTip().ReallocateCache();
}
return ret;
}
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index dd425c1160..fa6e56c6e4 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -4,10 +4,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/compat.h>
+#include <logging.h>
#include <util/fs.h>
+#include <util/time.h>
#include <wallet/bdb.h>
#include <wallet/db.h>
+#include <sync.h>
#include <util/check.h>
#include <util/fs_helpers.h>
#include <util/strencodings.h>
@@ -665,12 +668,14 @@ void BerkeleyDatabase::ReloadDbEnv()
env->ReloadDbEnv();
}
-BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database)
+BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database, BerkeleyBatch* batch)
{
if (!database.m_db.get()) {
throw std::runtime_error(STR_INTERNAL_BUG("BerkeleyDatabase does not exist"));
}
- int ret = database.m_db->cursor(nullptr, &m_cursor, 0);
+ // Transaction argument to cursor is only needed when using the cursor to
+ // write to the database. Read-only cursors do not need a txn pointer.
+ int ret = database.m_db->cursor(batch ? batch->txn() : nullptr, &m_cursor, 0);
if (ret != 0) {
throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret)));
}
@@ -817,6 +822,25 @@ bool BerkeleyBatch::HasKey(DataStream&& key)
return ret == 0;
}
+bool BerkeleyBatch::ErasePrefix(Span<const std::byte> prefix)
+{
+ if (!TxnBegin()) return false;
+ auto cursor{std::make_unique<BerkeleyCursor>(m_database, this)};
+ // const_cast is safe below even though prefix_key is an in/out parameter,
+ // because we are not using the DB_DBT_USERMEM flag, so BDB will allocate
+ // and return a different output data pointer
+ Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
+ int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
+ for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
+ SafeDbt key, value;
+ ret = cursor->dbc()->get(key, value, flag);
+ if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
+ ret = cursor->dbc()->del(0);
+ }
+ cursor.reset();
+ return TxnCommit() && (ret == 0 || ret == DB_NOTFOUND);
+}
+
void BerkeleyDatabase::AddRef()
{
LOCK(cs_db);
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index fbec0bb90b..4fac128bf1 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -14,6 +14,7 @@
#include <wallet/db.h>
#include <atomic>
+#include <condition_variable>
#include <map>
#include <memory>
#include <string>
@@ -191,10 +192,11 @@ private:
Dbc* m_cursor;
public:
- explicit BerkeleyCursor(BerkeleyDatabase& database);
+ explicit BerkeleyCursor(BerkeleyDatabase& database, BerkeleyBatch* batch=nullptr);
~BerkeleyCursor() override;
Status Next(DataStream& key, DataStream& value) override;
+ Dbc* dbc() const { return m_cursor; }
};
/** RAII class that provides access to a Berkeley database */
@@ -205,6 +207,7 @@ private:
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
bool EraseKey(DataStream&& key) override;
bool HasKey(DataStream&& key) override;
+ bool ErasePrefix(Span<const std::byte> prefix) override;
protected:
Db* pdb{nullptr};
@@ -229,6 +232,7 @@ public:
bool TxnBegin() override;
bool TxnCommit() override;
bool TxnAbort() override;
+ DbTxn* txn() const { return activeTxn; }
};
std::string BerkeleyDatabaseVersion();
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index 3b3c1f8da4..ad2fab4b7d 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -4,7 +4,7 @@
#include <wallet/coincontrol.h>
-#include <util/system.h>
+#include <common/args.h>
namespace wallet {
CCoinControl::CCoinControl()
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 9cf61dbe51..832d24746f 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -6,15 +6,24 @@
#include <consensus/amount.h>
#include <consensus/consensus.h>
+#include <logging.h>
#include <policy/feerate.h>
#include <util/check.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/system.h>
#include <numeric>
#include <optional>
+#include <queue>
namespace wallet {
+// Common selection error across the algorithms
+static util::Result<SelectionResult> ErrorMaxWeightExceeded()
+{
+ return util::Error{_("The inputs size exceeds the maximum weight. "
+ "Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
+}
+
// Descending order comparator
struct {
bool operator()(const OutputGroup& a, const OutputGroup& b) const
@@ -63,11 +72,13 @@ struct {
static const size_t TOTAL_TRIES = 100000;
-std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
+util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change,
+ int max_weight)
{
SelectionResult result(selection_target, SelectionAlgorithm::BNB);
CAmount curr_value = 0;
std::vector<size_t> curr_selection; // selected utxo indexes
+ int curr_selection_weight = 0; // sum of selected utxo weight
// Calculate curr_available_value
CAmount curr_available_value = 0;
@@ -78,7 +89,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
curr_available_value += utxo.GetSelectionAmount();
}
if (curr_available_value < selection_target) {
- return std::nullopt;
+ return util::Error();
}
// Sort the utxo_pool
@@ -89,6 +100,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
CAmount best_waste = MAX_MONEY;
bool is_feerate_high = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;
+ bool max_tx_weight_exceeded = false;
// Depth First search loop for choosing the UTXOs
for (size_t curr_try = 0, utxo_pool_index = 0; curr_try < TOTAL_TRIES; ++curr_try, ++utxo_pool_index) {
@@ -98,6 +110,9 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch
(curr_waste > best_waste && is_feerate_high)) { // Don't select things which we know will be more wasteful if the waste is increasing
backtrack = true;
+ } else if (curr_selection_weight > max_weight) { // Exceeding weight for standard tx, cannot find more solutions by adding more inputs
+ max_tx_weight_exceeded = true; // at least one selection attempt exceeded the max weight
+ backtrack = true;
} else if (curr_value >= selection_target) { // Selected value is within range
curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison
// Adding another UTXO after this check could bring the waste down if the long term fee is higher than the current fee.
@@ -127,6 +142,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
OutputGroup& utxo = utxo_pool.at(utxo_pool_index);
curr_value -= utxo.GetSelectionAmount();
curr_waste -= utxo.fee - utxo.long_term_fee;
+ curr_selection_weight -= utxo.m_weight;
curr_selection.pop_back();
} else { // Moving forwards, continuing down this branch
OutputGroup& utxo = utxo_pool.at(utxo_pool_index);
@@ -146,13 +162,14 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
curr_selection.push_back(utxo_pool_index);
curr_value += utxo.GetSelectionAmount();
curr_waste += utxo.fee - utxo.long_term_fee;
+ curr_selection_weight += utxo.m_weight;
}
}
}
// Check for solution
if (best_selection.empty()) {
- return std::nullopt;
+ return max_tx_weight_exceeded ? ErrorMaxWeightExceeded() : util::Error();
}
// Set output set
@@ -165,9 +182,20 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
return result;
}
-std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng)
+class MinOutputGroupComparator
+{
+public:
+ int operator() (const OutputGroup& group1, const OutputGroup& group2) const
+ {
+ return group1.GetSelectionAmount() > group2.GetSelectionAmount();
+ }
+};
+
+util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng,
+ int max_weight)
{
SelectionResult result(target_value, SelectionAlgorithm::SRD);
+ std::priority_queue<OutputGroup, std::vector<OutputGroup>, MinOutputGroupComparator> heap;
// Include change for SRD as we want to avoid making really small change if the selection just
// barely meets the target. Just use the lower bound change target instead of the randomly
@@ -181,16 +209,40 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut
Shuffle(indexes.begin(), indexes.end(), rng);
CAmount selected_eff_value = 0;
+ int weight = 0;
+ bool max_tx_weight_exceeded = false;
for (const size_t i : indexes) {
const OutputGroup& group = utxo_pool.at(i);
Assume(group.GetSelectionAmount() > 0);
+
+ // Add group to selection
+ heap.push(group);
selected_eff_value += group.GetSelectionAmount();
- result.AddInput(group);
+ weight += group.m_weight;
+
+ // If the selection weight exceeds the maximum allowed size, remove the least valuable inputs until we
+ // are below max weight.
+ if (weight > max_weight) {
+ max_tx_weight_exceeded = true; // mark it in case we don't find any useful result.
+ do {
+ const OutputGroup& to_remove_group = heap.top();
+ selected_eff_value -= to_remove_group.GetSelectionAmount();
+ weight -= to_remove_group.m_weight;
+ heap.pop();
+ } while (!heap.empty() && weight > max_weight);
+ }
+
+ // Now check if we are above the target
if (selected_eff_value >= target_value) {
+ // Result found, add it.
+ while (!heap.empty()) {
+ result.AddInput(heap.top());
+ heap.pop();
+ }
return result;
}
}
- return std::nullopt;
+ return max_tx_weight_exceeded ? ErrorMaxWeightExceeded() : util::Error();
}
/** Find a subset of the OutputGroups that is at least as large as, but as close as possible to, the
@@ -252,8 +304,8 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
}
}
-std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
- CAmount change_target, FastRandomContext& rng)
+util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
+ CAmount change_target, FastRandomContext& rng, int max_weight)
{
SelectionResult result(nTargetValue, SelectionAlgorithm::KNAPSACK);
@@ -286,7 +338,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
}
if (nTotalLower < nTargetValue) {
- if (!lowest_larger) return std::nullopt;
+ if (!lowest_larger) return util::Error();
result.AddInput(*lowest_larger);
return result;
}
@@ -313,6 +365,16 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
}
}
+ // If the result exceeds the maximum allowed size, return closest UTXO above the target
+ if (result.GetWeight() > max_weight) {
+ // No coin above target, nothing to do.
+ if (!lowest_larger) return ErrorMaxWeightExceeded();
+
+ // Return closest UTXO above target
+ result.Clear();
+ result.AddInput(*lowest_larger);
+ }
+
if (LogAcceptCategory(BCLog::SELECTCOINS, BCLog::Level::Debug)) {
std::string log_message{"Coin selection best subset: "};
for (unsigned int i = 0; i < applicable_groups.size(); i++) {
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 5a7b748be1..723f5bbfb3 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -13,6 +13,7 @@
#include <random.h>
#include <util/system.h>
#include <util/check.h>
+#include <util/result.h>
#include <optional>
@@ -408,20 +409,24 @@ public:
int GetWeight() const { return m_weight; }
};
-std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change);
+util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change,
+ int max_weight);
/** Select coins by Single Random Draw. OutputGroups are selected randomly from the eligible
* outputs until the target is satisfied
*
* @param[in] utxo_pool The positive effective value OutputGroups eligible for selection
* @param[in] target_value The target value to select for
- * @returns If successful, a SelectionResult, otherwise, std::nullopt
+ * @param[in] rng The randomness source to shuffle coins
+ * @param[in] max_weight The maximum allowed weight for a selection result to be valid
+ * @returns If successful, a valid SelectionResult, otherwise, util::Error
*/
-std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng);
+util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng,
+ int max_weight);
// Original coin selection algorithm as a fallback
-std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
- CAmount change_target, FastRandomContext& rng);
+util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
+ CAmount change_target, FastRandomContext& rng, int max_weight);
} // namespace wallet
#endif // BITCOIN_WALLET_COINSELECTION_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 92e1d61ac4..0c24920516 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -4,9 +4,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/args.h>
#include <logging.h>
#include <util/fs.h>
-#include <util/system.h>
#include <wallet/db.h>
#include <exception>
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 2da94885be..6834ba6963 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -110,6 +110,7 @@ public:
return HasKey(std::move(ssKey));
}
+ virtual bool ErasePrefix(Span<const std::byte> prefix) = 0;
virtual std::unique_ptr<DatabaseCursor> GetNewCursor() = 0;
virtual bool TxnBegin() = 0;
@@ -186,6 +187,7 @@ private:
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return true; }
bool EraseKey(DataStream&& key) override { return true; }
bool HasKey(DataStream&& key) override { return true; }
+ bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
public:
void Flush() override {}
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 31b6eb4ae1..865af0bb23 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -4,8 +4,8 @@
#include <wallet/dump.h>
+#include <common/args.h>
#include <util/fs.h>
-#include <util/system.h>
#include <util/translation.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index cb861d835e..079e3baa4e 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -3,7 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/args.h>
#include <external_signer.h>
+#include <util/system.h>
#include <wallet/external_signer_scriptpubkeyman.h>
#include <iostream>
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 5403e38950..0d0a8650ac 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/init.h>
@@ -14,7 +15,6 @@
#include <univalue.h>
#include <util/check.h>
#include <util/moneystr.h>
-#include <util/system.h>
#include <util/translation.h>
#ifdef USE_BDB
#include <wallet/bdb.h>
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 086f6d9de8..cd438cfe2f 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -4,6 +4,7 @@
#include <interfaces/wallet.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
@@ -15,7 +16,6 @@
#include <sync.h>
#include <uint256.h>
#include <util/check.h>
-#include <util/system.h>
#include <util/translation.h>
#include <util/ui_change_type.h>
#include <wallet/coincontrol.h>
@@ -228,9 +228,22 @@ public:
return m_wallet->GetAddressReceiveRequests();
}
bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) override {
+ // Note: The setAddressReceiveRequest interface used by the GUI to store
+ // receive requests is a little awkward and could be improved in the
+ // future:
+ //
+ // - The same method is used to save requests and erase them, but
+ // having separate methods could be clearer and prevent bugs.
+ //
+ // - Request ids are passed as strings even though they are generated as
+ // integers.
+ //
+ // - Multiple requests can be stored for the same address, but it might
+ // be better to only allow one request or only keep the current one.
LOCK(m_wallet->cs_wallet);
WalletBatch batch{m_wallet->GetDatabase()};
- return m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
+ return value.empty() ? m_wallet->EraseAddressReceiveRequest(batch, dest, id)
+ : m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
}
bool displayAddress(const CTxDestination& dest) override
{
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 2103fc64e4..2560dda87c 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -5,12 +5,12 @@
#include <wallet/load.h>
+#include <common/args.h>
#include <interfaces/chain.h>
#include <scheduler.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/translation.h>
#include <wallet/context.h>
#include <wallet/spend.h>
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index 633f606747..a5be0739a9 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -218,7 +218,8 @@ RPCHelpMan addmultisigaddress()
"Each key is a Bitcoin address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
"See `importaddress` for watchonly p2sh address support.\n"
- "If 'label' is specified, assign address to that label.\n",
+ "If 'label' is specified, assign address to that label.\n"
+ "Note: This command is only compatible with legacy wallets.\n",
{
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The bitcoin addresses or hex-encoded public keys",
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index d9712f05d2..553bbfb62f 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -119,7 +119,8 @@ RPCHelpMan importprivkey()
"may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
"but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
+ "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" with \"combo(X)\" for descriptor wallets.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
{"label", RPCArg::Type::STR, RPCArg::DefaultHint{"current label if address exists, otherwise \"\""}, "An optional label"},
@@ -225,7 +226,7 @@ RPCHelpMan importaddress()
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
"as change, and not show up in many RPCs.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n"
- "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" with \"addr(X)\" for descriptor wallets.\n",
+ "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" for descriptor wallets.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
@@ -417,7 +418,8 @@ RPCHelpMan importpubkey()
"may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
"but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
+ "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" with \"combo(X)\" for descriptor wallets.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
@@ -495,7 +497,8 @@ RPCHelpMan importwallet()
{
return RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
- "Note: Blockchain and Mempool will be rescanned after a successful import. Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Blockchain and Mempool will be rescanned after a successful import. Use \"getwalletinfo\" to query the scanning progress.\n"
+ "Note: This command is only compatible with legacy wallets.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
@@ -642,7 +645,8 @@ RPCHelpMan dumpprivkey()
{
return RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
- "Then the importprivkey can be used with this output\n",
+ "Then the importprivkey can be used with this output\n"
+ "Note: This command is only compatible with legacy wallets.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
},
@@ -690,7 +694,8 @@ RPCHelpMan dumpwallet()
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
"Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
- "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
+ "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
+ "Note: This command is only compatible with legacy wallets.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (absolute path recommended)"},
},
@@ -1253,7 +1258,8 @@ RPCHelpMan importmulti()
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
"but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
+ "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" for descriptor wallets.\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 5c9de7de47..eb4f4c87ae 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -389,6 +389,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
entry.pushKV("label", label);
}
entry.pushKV("vout", r.vout);
+ entry.pushKV("abandoned", wtx.isAbandoned());
if (fLong)
WalletTxToJSON(wallet, wtx, entry);
ret.push_back(entry);
@@ -462,8 +463,7 @@ RPCHelpMan listtransactions()
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", /*optional=*/true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- "'send' category of transactions."},
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
})},
}
},
@@ -576,8 +576,7 @@ RPCHelpMan listsinceblock()
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", /*optional=*/true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- "'send' category of transactions."},
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
{RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
})},
}},
@@ -721,8 +720,7 @@ RPCHelpMan gettransaction()
{RPCResult::Type::NUM, "vout", "the vout value"},
{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
"'send' category of transactions."},
- {RPCResult::Type::BOOL, "abandoned", /*optional=*/true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- "'send' category of transactions."},
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
{RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'received'. List of parent descriptors for the scriptPubKey of this coin.", {
{RPCResult::Type::STR, "desc", "The descriptor string."},
}},
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index ea3507bc75..7e57740706 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -504,8 +504,8 @@ static RPCHelpMan sethdseed()
return RPCHelpMan{"sethdseed",
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
- "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
- HELP_REQUIRING_PASSPHRASE,
+ "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." + HELP_REQUIRING_PASSPHRASE +
+ "Note: This command is only compatible with legacy wallets.\n",
{
{"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
"If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 57f3785a3a..748f40dce8 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <algorithm>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/validation.h>
#include <interfaces/chain.h>
@@ -14,6 +15,7 @@
#include <util/fees.h>
#include <util/moneystr.h>
#include <util/rbf.h>
+#include <util/system.h>
#include <util/trace.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
@@ -559,42 +561,45 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
{
// Vector of results. We will choose the best one based on waste.
std::vector<SelectionResult> results;
+ std::vector<util::Result<SelectionResult>> errors;
+ auto append_error = [&] (const util::Result<SelectionResult>& result) {
+ // If any specific error message appears here, then something different from a simple "no selection found" happened.
+ // Let's save it, so it can be retrieved to the user if no other selection algorithm succeeded.
+ if (HasErrorMsg(result)) {
+ errors.emplace_back(result);
+ }
+ };
+
+ // Maximum allowed weight
+ int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
- if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change)}) {
+ if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
results.push_back(*bnb_result);
- }
+ } else append_error(bnb_result);
+
+ // As Knapsack and SRD can create change, also deduce change weight.
+ max_inputs_weight -= (coin_selection_params.change_output_size * WITNESS_SCALE_FACTOR);
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
- if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
+ if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast, max_inputs_weight)}) {
knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
results.push_back(*knapsack_result);
- }
+ } else append_error(knapsack_result);
- if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast)}) {
+ if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast, max_inputs_weight)}) {
srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
results.push_back(*srd_result);
- }
+ } else append_error(srd_result);
if (results.empty()) {
- // No solution found
- return util::Error();
- }
-
- std::vector<SelectionResult> eligible_results;
- std::copy_if(results.begin(), results.end(), std::back_inserter(eligible_results), [coin_selection_params](const SelectionResult& result) {
- const auto initWeight{coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR};
- return initWeight + result.GetWeight() <= static_cast<int>(MAX_STANDARD_TX_WEIGHT);
- });
-
- if (eligible_results.empty()) {
- return util::Error{_("The inputs size exceeds the maximum weight. "
- "Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
+ // No solution found, retrieve the first explicit error (if any).
+ // future: add 'severity level' to errors so the worst one can be retrieved instead of the first one.
+ return errors.empty() ? util::Error() : errors.front();
}
// Choose the result with the least waste
// If the waste is the same, choose the one which spends more inputs.
- auto& best_result = *std::min_element(eligible_results.begin(), eligible_results.end());
- return best_result;
+ return *std::min_element(results.begin(), results.end());
}
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs,
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index d6259e095e..77e8a4e9c1 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -125,6 +125,7 @@ void SQLiteBatch::SetupSQLStatements()
{&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"},
{&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"},
{&m_delete_stmt, "DELETE FROM main WHERE key = ?"},
+ {&m_delete_prefix_stmt, "DELETE FROM main WHERE instr(key, ?) = 1"},
};
for (const auto& [stmt_prepared, stmt_text] : statements) {
@@ -375,6 +376,7 @@ void SQLiteBatch::Close()
{&m_insert_stmt, "insert"},
{&m_overwrite_stmt, "overwrite"},
{&m_delete_stmt, "delete"},
+ {&m_delete_prefix_stmt, "delete prefix"},
};
for (const auto& [stmt_prepared, stmt_description] : statements) {
@@ -441,24 +443,34 @@ bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
return res == SQLITE_DONE;
}
-bool SQLiteBatch::EraseKey(DataStream&& key)
+bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, Span<const std::byte> blob)
{
if (!m_database.m_db) return false;
- assert(m_delete_stmt);
+ assert(stmt);
// Bind: leftmost parameter in statement is index 1
- if (!BindBlobToStatement(m_delete_stmt, 1, key, "key")) return false;
+ if (!BindBlobToStatement(stmt, 1, blob, "key")) return false;
// Execute
- int res = sqlite3_step(m_delete_stmt);
- sqlite3_clear_bindings(m_delete_stmt);
- sqlite3_reset(m_delete_stmt);
+ int res = sqlite3_step(stmt);
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
if (res != SQLITE_DONE) {
LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
}
return res == SQLITE_DONE;
}
+bool SQLiteBatch::EraseKey(DataStream&& key)
+{
+ return ExecStatement(m_delete_stmt, key);
+}
+
+bool SQLiteBatch::ErasePrefix(Span<const std::byte> prefix)
+{
+ return ExecStatement(m_delete_prefix_stmt, prefix);
+}
+
bool SQLiteBatch::HasKey(DataStream&& key)
{
if (!m_database.m_db) return false;
diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h
index 5745a1d4cf..d9de40569b 100644
--- a/src/wallet/sqlite.h
+++ b/src/wallet/sqlite.h
@@ -36,13 +36,16 @@ private:
sqlite3_stmt* m_insert_stmt{nullptr};
sqlite3_stmt* m_overwrite_stmt{nullptr};
sqlite3_stmt* m_delete_stmt{nullptr};
+ sqlite3_stmt* m_delete_prefix_stmt{nullptr};
void SetupSQLStatements();
+ bool ExecStatement(sqlite3_stmt* stmt, Span<const std::byte> blob);
bool ReadKey(DataStream&& key, DataStream& value) override;
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
bool EraseKey(DataStream&& key) override;
bool HasKey(DataStream&& key) override;
+ bool ErasePrefix(Span<const std::byte> prefix) override;
public:
explicit SQLiteBatch(SQLiteDatabase& database);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 8f626addde..7f66179517 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -68,7 +68,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
set.insert(std::make_shared<COutput>(coin));
}
-static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput =0, bool spendable = false)
+static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput =0, bool spendable = false, int custom_size = 0)
{
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -84,7 +84,21 @@ static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmoun
assert(ret.second);
CWalletTx& wtx = (*ret.first).second;
const auto& txout = wtx.tx->vout.at(nInput);
- available_coins.Add(OutputType::BECH32, {COutPoint(wtx.GetHash(), nInput), txout, nAge, CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate});
+ available_coins.Add(OutputType::BECH32, {COutPoint(wtx.GetHash(), nInput), txout, nAge, custom_size == 0 ? CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr) : custom_size, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate});
+}
+
+// Helpers
+std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
+ CAmount change_target, FastRandomContext& rng)
+{
+ auto res{KnapsackSolver(groups, nTargetValue, change_target, rng, MAX_STANDARD_TX_WEIGHT)};
+ return res ? std::optional<SelectionResult>(*res) : std::nullopt;
+}
+
+std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
+{
+ auto res{SelectCoinsBnB(utxo_pool, selection_target, cost_of_change, MAX_STANDARD_TX_WEIGHT)};
+ return res ? std::optional<SelectionResult>(*res) : std::nullopt;
}
/** Check if SelectionResult a is equivalent to SelectionResult b.
@@ -128,13 +142,15 @@ static CAmount make_hard_case(int utxos, std::vector<COutput>& utxo_pool)
return target;
}
-inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& available_coins)
+inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& available_coins, bool subtract_fee_outputs = false)
{
static std::vector<OutputGroup> static_groups;
static_groups.clear();
for (auto& coin : available_coins) {
static_groups.emplace_back();
- static_groups.back().Insert(std::make_shared<COutput>(coin), /*ancestors=*/ 0, /*descendants=*/ 0);
+ OutputGroup& group = static_groups.back();
+ group.Insert(std::make_shared<COutput>(coin), /*ancestors=*/ 0, /*descendants=*/ 0);
+ group.m_subtract_fee_outputs = subtract_fee_outputs;
}
return static_groups;
}
@@ -158,6 +174,16 @@ inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinsResult& availab
return static_groups.all_groups.mixed_group;
}
+static std::unique_ptr<CWallet> NewWallet(const node::NodeContext& m_node, const std::string& wallet_name = "")
+{
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), wallet_name, CreateMockWalletDatabase());
+ BOOST_CHECK(wallet->LoadWallet() == DBErrors::LOAD_OK);
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+ return wallet;
+}
+
// Branch and bound coin selection tests
BOOST_AUTO_TEST_CASE(bnb_search_test)
{
@@ -294,11 +320,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_selection_params_bnb.m_subtract_fee_outputs = true;
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
CoinsResult available_coins;
@@ -316,11 +338,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
CoinsResult available_coins;
@@ -335,15 +353,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
selected_input.Insert(select_coin, coin_selection_params_bnb.m_subtract_fee_outputs);
available_coins.Erase({available_coins.coins[OutputType::BECH32].begin()->outpoint});
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
+ LOCK(wallet->cs_wallet);
const auto result10 = SelectCoins(*wallet, available_coins, selected_input, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(result10);
}
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
+ LOCK(wallet->cs_wallet); // Every 'SelectCoins' call requires it
CoinsResult available_coins;
@@ -397,6 +413,37 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
const auto result13 = SelectCoins(*wallet, available_coins, selected_input, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result13));
}
+
+ {
+ // Test bnb max weight exceeded
+ // Inputs set [10, 9, 8, 5, 3, 1], Selection Target = 16 and coin 5 exceeding the max weight.
+
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
+
+ CoinsResult available_coins;
+ add_coin(available_coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 8 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true, /*custom_size=*/MAX_STANDARD_TX_WEIGHT);
+ add_coin(available_coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ CAmount selection_target = 16 * CENT;
+ const auto& no_res = SelectCoinsBnB(GroupCoins(available_coins.All(), /*subtract_fee_outputs*/true),
+ selection_target, /*cost_of_change=*/0, MAX_STANDARD_TX_WEIGHT);
+ BOOST_ASSERT(!no_res);
+ BOOST_CHECK(util::ErrorString(no_res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);
+
+ // Now add same coin value with a good size and check that it gets selected
+ add_coin(available_coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ const auto& res = SelectCoinsBnB(GroupCoins(available_coins.All(), /*subtract_fee_outputs*/true), selection_target, /*cost_of_change=*/0);
+
+ expected_result.Clear();
+ add_coin(8 * CENT, 2, expected_result);
+ add_coin(5 * CENT, 2, expected_result);
+ add_coin(3 * CENT, 2, expected_result);
+ BOOST_CHECK(EquivalentResult(expected_result, *res));
+ }
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
@@ -404,11 +451,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
FastRandomContext rand{};
const auto temp1{[&rand](std::vector<OutputGroup>& g, const CAmount& v, CAmount c) { return KnapsackSolver(g, v, c, rand); }};
const auto KnapsackSolver{temp1};
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
CoinsResult available_coins;
@@ -714,11 +757,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
{
FastRandomContext rand{};
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
CoinsResult available_coins;
@@ -736,11 +775,8 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
+ LOCK(wallet->cs_wallet); // Every 'SelectCoins' call requires it
// Random generator stuff
std::default_random_engine generator;
@@ -921,16 +957,101 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
}
-static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, interfaces::Chain* chain)
+
+static util::Result<SelectionResult> SelectCoinsSRD(const CAmount& target,
+ const CoinSelectionParams& cs_params,
+ const node::NodeContext& m_node,
+ int max_weight,
+ std::function<CoinsResult(CWallet&)> coin_setup)
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(chain, "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
+ CoinEligibilityFilter filter(0, 0, 0); // accept all coins without ancestors
+ Groups group = GroupOutputs(*wallet, coin_setup(*wallet), cs_params, {{filter}})[filter].all_groups;
+ return SelectCoinsSRD(group.positive_group, target, cs_params.rng_fast, max_weight);
+}
+
+BOOST_AUTO_TEST_CASE(srd_tests)
+{
+ // Test SRD:
+ // 1) Insufficient funds, select all provided coins and fail.
+ // 2) Exceeded max weight, coin selection always surpasses the max allowed weight.
+ // 3) Select coins without surpassing the max weight (some coins surpasses the max allowed weight, some others not)
+
+ FastRandomContext rand;
+ CoinSelectionParams dummy_params{ // Only used to provide the 'avoid_partial' flag.
+ rand,
+ /*change_output_size=*/34,
+ /*change_spend_size=*/68,
+ /*min_change_target=*/CENT,
+ /*effective_feerate=*/CFeeRate(0),
+ /*long_term_feerate=*/CFeeRate(0),
+ /*discard_feerate=*/CFeeRate(0),
+ /*tx_noinputs_size=*/10 + 34, // static header size + output size
+ /*avoid_partial=*/false,
+ };
+
+ {
+ // #########################################################
+ // 1) Insufficient funds, select all provided coins and fail
+ // #########################################################
+ CAmount target = 49.5L * COIN;
+ int max_weight = 10000; // high enough to not fail for this reason.
+ const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
+ CoinsResult available_coins;
+ for (int j = 0; j < 10; ++j) {
+ add_coin(available_coins, wallet, CAmount(1 * COIN));
+ add_coin(available_coins, wallet, CAmount(2 * COIN));
+ }
+ return available_coins;
+ });
+ BOOST_CHECK(!res);
+ BOOST_CHECK(util::ErrorString(res).empty()); // empty means "insufficient funds"
+ }
+
+ {
+ // ###########################
+ // 2) Test max weight exceeded
+ // ###########################
+ CAmount target = 49.5L * COIN;
+ int max_weight = 3000;
+ const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
+ CoinsResult available_coins;
+ for (int j = 0; j < 10; ++j) {
+ add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(0), 144, false, 0, true);
+ add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
+ }
+ return available_coins;
+ });
+ BOOST_CHECK(!res);
+ BOOST_CHECK(util::ErrorString(res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);
+ }
+ {
+ // ################################################################################################################
+ // 3) Test selection when some coins surpass the max allowed weight while others not. --> must find a good solution
+ // ################################################################################################################
+ CAmount target = 25.33L * COIN;
+ int max_weight = 10000; // WU
+ const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
+ CoinsResult available_coins;
+ for (int j = 0; j < 60; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU
+ add_coin(available_coins, wallet, CAmount(0.33 * COIN), CFeeRate(0), 144, false, 0, true);
+ }
+ for (int i = 0; i < 10; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU
+ add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
+ }
+ return available_coins;
+ });
+ BOOST_CHECK(res);
+ }
+}
+
+static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, const node::NodeContext& m_node)
+{
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
auto available_coins = coin_setup(*wallet);
+ LOCK(wallet->cs_wallet);
auto result = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/ {}, target, cc, cs_params);
if (result) {
const auto signedTxSize = 10 + 34 + 68 * result->GetInputSet().size(); // static header size + output size + inputs size (P2WPKH)
@@ -964,8 +1085,6 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
/*avoid_partial=*/false,
};
- auto chain{m_node.chain.get()};
-
{
// Scenario 1:
// The actor starts with 1x 50.0 BTC and 1515x 0.033 BTC (~100.0 BTC total) unspent outputs
@@ -984,10 +1103,13 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
add_coin(available_coins, wallet, CAmount(50 * COIN), CFeeRate(0), 144, false, 0, true);
return available_coins;
},
- chain);
+ m_node);
BOOST_CHECK(result);
- BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(50 * COIN)));
+ // Verify that only the 50 BTC UTXO was selected
+ const auto& selection_res = result->GetInputSet();
+ BOOST_CHECK(selection_res.size() == 1);
+ BOOST_CHECK((*selection_res.begin())->GetEffectiveValue() == 50 * COIN);
}
{
@@ -1009,7 +1131,7 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
}
return available_coins;
},
- chain);
+ m_node);
BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(0.0625 * COIN)));
BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(0.025 * COIN)));
@@ -1030,7 +1152,7 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
}
return available_coins;
},
- chain);
+ m_node);
// No results
// 1515 inputs * 68 bytes = 103,020 bytes
@@ -1045,20 +1167,11 @@ BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
// This test creates a coin whose value is higher than the target but whose effective value is lower than the target.
// The coin is selected using coin control, with m_allow_other_inputs = false. SelectCoins should fail due to insufficient funds.
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- LOCK(wallet->cs_wallet);
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- wallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> wallet = NewWallet(m_node);
CoinsResult available_coins;
{
- std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", CreateMockWalletDatabase());
- dummyWallet->LoadWallet();
- LOCK(dummyWallet->cs_wallet);
- dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- dummyWallet->SetupDescriptorScriptPubKeyMans();
-
+ std::unique_ptr<CWallet> dummyWallet = NewWallet(m_node, /*wallet_name=*/"dummy");
add_coin(available_coins, *dummyWallet, 100000); // 0.001 BTC
}
@@ -1082,6 +1195,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
cc.SetInputWeight(output.outpoint, 148);
cc.SelectExternal(output.outpoint, output.txout);
+ LOCK(wallet->cs_wallet);
const auto preset_inputs = *Assert(FetchSelectedInputs(*wallet, cc, cs_params));
available_coins.Erase({available_coins.coins[OutputType::BECH32].begin()->outpoint});
@@ -1094,11 +1208,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_coinsresult_test, BasicTestingSetup)
// Test case to verify CoinsResult object sanity.
CoinsResult available_coins;
{
- std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", CreateMockWalletDatabase());
- BOOST_CHECK_EQUAL(dummyWallet->LoadWallet(), DBErrors::LOAD_OK);
- LOCK(dummyWallet->cs_wallet);
- dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- dummyWallet->SetupDescriptorScriptPubKeyMans();
+ std::unique_ptr<CWallet> dummyWallet = NewWallet(m_node, /*wallet_name=*/"dummy");
// Add some coins to 'available_coins'
for (int i=0; i<10; i++) {
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 304190eec1..9be8efab62 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/feerate.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -44,6 +45,9 @@ static void GroupCoins(FuzzedDataProvider& fuzzed_data_provider, const std::vect
if (valid_outputgroup) output_groups.push_back(output_group);
}
+// Returns true if the result contains an error and the message is not empty
+static bool HasErrorMsg(const util::Result<SelectionResult>& res) { return !util::ErrorString(res).empty(); }
+
FUZZ_TARGET(coinselection)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
@@ -84,18 +88,18 @@ FUZZ_TARGET(coinselection)
GroupCoins(fuzzed_data_provider, utxo_pool, coin_params, /*positive_only=*/false, group_all);
// Run coinselection algorithms
- const auto result_bnb = SelectCoinsBnB(group_pos, target, cost_of_change);
+ const auto result_bnb = SelectCoinsBnB(group_pos, target, cost_of_change, MAX_STANDARD_TX_WEIGHT);
- auto result_srd = SelectCoinsSRD(group_pos, target, fast_random_context);
+ auto result_srd = SelectCoinsSRD(group_pos, target, fast_random_context, MAX_STANDARD_TX_WEIGHT);
if (result_srd) result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
CAmount change_target{GenerateChangeTarget(target, coin_params.m_change_fee, fast_random_context)};
- auto result_knapsack = KnapsackSolver(group_all, target, change_target, fast_random_context);
+ auto result_knapsack = KnapsackSolver(group_all, target, change_target, fast_random_context, MAX_STANDARD_TX_WEIGHT);
if (result_knapsack) result_knapsack->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
// If the total balance is sufficient for the target and we are not using
- // effective values, Knapsack should always find a solution.
- if (total_balance >= target && subtract_fee_outputs) {
+ // effective values, Knapsack should always find a solution (unless the selection exceeded the max tx weight).
+ if (total_balance >= target && subtract_fee_outputs && !HasErrorMsg(result_knapsack)) {
assert(result_knapsack);
}
}
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 1db2a06bf8..0adc63876c 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <common/args.h>
#include <univalue.h>
#include <util/check.h>
#include <util/fs.h>
-#include <util/system.h>
#include <fstream>
#include <string>
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 3dbc91fc68..10aeed49cb 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -4,10 +4,10 @@
#include <boost/test/unit_test.hpp>
+#include <common/args.h>
#include <noui.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
-#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
namespace wallet {
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index edcfaa24e5..da4b553394 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -427,19 +427,63 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
}
-BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
+static const DatabaseFormat DATABASE_FORMATS[] = {
+#ifdef USE_SQLITE
+ DatabaseFormat::SQLITE,
+#endif
+#ifdef USE_BDB
+ DatabaseFormat::BERKELEY,
+#endif
+};
+
+void TestLoadWallet(const std::string& name, DatabaseFormat format, std::function<void(std::shared_ptr<CWallet>)> f)
{
- CTxDestination dest = PKHash();
- LOCK(m_wallet.cs_wallet);
- WalletBatch batch{m_wallet.GetDatabase()};
- m_wallet.SetAddressUsed(batch, dest, true);
- m_wallet.SetAddressReceiveRequest(batch, dest, "0", "val_rr0");
- m_wallet.SetAddressReceiveRequest(batch, dest, "1", "val_rr1");
-
- auto values = m_wallet.GetAddressReceiveRequests();
- BOOST_CHECK_EQUAL(values.size(), 2U);
- BOOST_CHECK_EQUAL(values[0], "val_rr0");
- BOOST_CHECK_EQUAL(values[1], "val_rr1");
+ node::NodeContext node;
+ auto chain{interfaces::MakeChain(node)};
+ DatabaseOptions options;
+ options.require_format = format;
+ DatabaseStatus status;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
+ auto database{MakeWalletDatabase(name, options, status, error)};
+ auto wallet{std::make_shared<CWallet>(chain.get(), "", std::move(database))};
+ BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
+ WITH_LOCK(wallet->cs_wallet, f(wallet));
+}
+
+BOOST_FIXTURE_TEST_CASE(LoadReceiveRequests, TestingSetup)
+{
+ for (DatabaseFormat format : DATABASE_FORMATS) {
+ const std::string name{strprintf("receive-requests-%i", format)};
+ TestLoadWallet(name, format, [](std::shared_ptr<CWallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) {
+ BOOST_CHECK(!wallet->IsAddressPreviouslySpent(PKHash()));
+ WalletBatch batch{wallet->GetDatabase()};
+ BOOST_CHECK(batch.WriteAddressPreviouslySpent(PKHash(), true));
+ BOOST_CHECK(batch.WriteAddressPreviouslySpent(ScriptHash(), true));
+ BOOST_CHECK(wallet->SetAddressReceiveRequest(batch, PKHash(), "0", "val_rr00"));
+ BOOST_CHECK(wallet->EraseAddressReceiveRequest(batch, PKHash(), "0"));
+ BOOST_CHECK(wallet->SetAddressReceiveRequest(batch, PKHash(), "1", "val_rr10"));
+ BOOST_CHECK(wallet->SetAddressReceiveRequest(batch, PKHash(), "1", "val_rr11"));
+ BOOST_CHECK(wallet->SetAddressReceiveRequest(batch, ScriptHash(), "2", "val_rr20"));
+ });
+ TestLoadWallet(name, format, [](std::shared_ptr<CWallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) {
+ BOOST_CHECK(wallet->IsAddressPreviouslySpent(PKHash()));
+ BOOST_CHECK(wallet->IsAddressPreviouslySpent(ScriptHash()));
+ auto requests = wallet->GetAddressReceiveRequests();
+ auto erequests = {"val_rr11", "val_rr20"};
+ BOOST_CHECK_EQUAL_COLLECTIONS(requests.begin(), requests.end(), std::begin(erequests), std::end(erequests));
+ WalletBatch batch{wallet->GetDatabase()};
+ BOOST_CHECK(batch.WriteAddressPreviouslySpent(PKHash(), false));
+ BOOST_CHECK(batch.EraseAddressData(ScriptHash()));
+ });
+ TestLoadWallet(name, format, [](std::shared_ptr<CWallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) {
+ BOOST_CHECK(!wallet->IsAddressPreviouslySpent(PKHash()));
+ BOOST_CHECK(!wallet->IsAddressPreviouslySpent(ScriptHash()));
+ auto requests = wallet->GetAddressReceiveRequests();
+ auto erequests = {"val_rr11"};
+ BOOST_CHECK_EQUAL_COLLECTIONS(requests.begin(), requests.end(), std::begin(erequests), std::end(erequests));
+ });
+ }
}
// Test some watch-only LegacyScriptPubKeyMan methods by the procedure of loading (LoadWatchOnly),
@@ -922,6 +966,7 @@ private:
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return m_pass; }
bool EraseKey(DataStream&& key) override { return m_pass; }
bool HasKey(DataStream&& key) override { return m_pass; }
+ bool ErasePrefix(Span<const std::byte> prefix) override { return m_pass; }
public:
explicit FailBatch(bool pass) : m_pass(pass) {}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4e8c0c0e5e..caf95a3f03 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -7,6 +7,7 @@
#include <blockfilter.h>
#include <chain.h>
+#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
@@ -36,7 +37,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
@@ -967,11 +967,11 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned
CTxDestination dst;
if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
if (IsMine(dst)) {
- if (used != IsAddressUsed(dst)) {
+ if (used != IsAddressPreviouslySpent(dst)) {
if (used) {
tx_destinations.insert(dst);
}
- SetAddressUsed(batch, dst, used);
+ SetAddressPreviouslySpent(batch, dst, used);
}
}
}
@@ -984,7 +984,7 @@ bool CWallet::IsSpentKey(const CScript& scriptPubKey) const
if (!ExtractDestination(scriptPubKey, dest)) {
return false;
}
- if (IsAddressUsed(dest)) {
+ if (IsAddressPreviouslySpent(dest)) {
return true;
}
if (IsLegacy()) {
@@ -992,15 +992,15 @@ bool CWallet::IsSpentKey(const CScript& scriptPubKey) const
assert(spk_man != nullptr);
for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) {
WitnessV0KeyHash wpkh_dest(keyid);
- if (IsAddressUsed(wpkh_dest)) {
+ if (IsAddressPreviouslySpent(wpkh_dest)) {
return true;
}
ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest));
- if (IsAddressUsed(sh_wpkh_dest)) {
+ if (IsAddressPreviouslySpent(sh_wpkh_dest)) {
return true;
}
PKHash pkh_dest(keyid);
- if (IsAddressUsed(pkh_dest)) {
+ if (IsAddressPreviouslySpent(pkh_dest)) {
return true;
}
}
@@ -2184,34 +2184,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
}
}
- // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
- if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
- // Figure out if any non_witness_utxos should be dropped
- std::vector<unsigned int> to_drop;
- for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
- const auto& input = psbtx.inputs.at(i);
- int wit_ver;
- std::vector<unsigned char> wit_prog;
- if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
- // There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
- to_drop.clear();
- break;
- }
- if (wit_ver == 0) {
- // Segwit v0, so we cannot drop any non_witness_utxos
- to_drop.clear();
- break;
- }
- if (input.non_witness_utxo) {
- to_drop.push_back(i);
- }
- }
-
- // Drop the non_witness_utxos that we can drop
- for (unsigned int i : to_drop) {
- psbtx.inputs.at(i).non_witness_utxo = nullptr;
- }
- }
+ RemoveUnnecessaryTransactions(psbtx, sighash_type);
// Complete if every input is now signed
complete = true;
@@ -2430,19 +2403,15 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
WalletBatch batch(GetDatabase());
{
LOCK(cs_wallet);
- // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
- // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
- // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ // If we want to delete receiving addresses, we should avoid calling EraseAddressData because it will delete the previously_spent value. Could instead just erase the label so it becomes a change address, and keep the data.
+ // NOTE: This isn't a problem for sending addresses because they don't have any data that needs to be kept.
+ // When adding new address data, it should be considered here whether to retain or delete it.
if (IsMine(address)) {
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
return false;
}
- // Delete destdata tuples associated with address
- std::string strAddress = EncodeDestination(address);
- for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
- {
- batch.EraseDestData(strAddress, item.first);
- }
+ // Delete data rows associated with this address
+ batch.EraseAddressData(address);
m_address_book.erase(address);
}
@@ -2817,51 +2786,42 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old
return nTimeSmart;
}
-bool CWallet::SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used)
+bool CWallet::SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used)
{
- const std::string key{"used"};
if (std::get_if<CNoDestination>(&dest))
return false;
if (!used) {
- if (auto* data = util::FindKey(m_address_book, dest)) data->destdata.erase(key);
- return batch.EraseDestData(EncodeDestination(dest), key);
+ if (auto* data{util::FindKey(m_address_book, dest)}) data->previously_spent = false;
+ return batch.WriteAddressPreviouslySpent(dest, false);
}
- const std::string value{"1"};
- m_address_book[dest].destdata.insert(std::make_pair(key, value));
- return batch.WriteDestData(EncodeDestination(dest), key, value);
+ LoadAddressPreviouslySpent(dest);
+ return batch.WriteAddressPreviouslySpent(dest, true);
}
-void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
+void CWallet::LoadAddressPreviouslySpent(const CTxDestination& dest)
{
- m_address_book[dest].destdata.insert(std::make_pair(key, value));
+ m_address_book[dest].previously_spent = true;
}
-bool CWallet::IsAddressUsed(const CTxDestination& dest) const
+void CWallet::LoadAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& request)
{
- const std::string key{"used"};
- std::map<CTxDestination, CAddressBookData>::const_iterator i = m_address_book.find(dest);
- if(i != m_address_book.end())
- {
- CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
- if(j != i->second.destdata.end())
- {
- return true;
- }
- }
+ m_address_book[dest].receive_requests[id] = request;
+}
+
+bool CWallet::IsAddressPreviouslySpent(const CTxDestination& dest) const
+{
+ if (auto* data{util::FindKey(m_address_book, dest)}) return data->previously_spent;
return false;
}
std::vector<std::string> CWallet::GetAddressReceiveRequests() const
{
- const std::string prefix{"rr"};
std::vector<std::string> values;
- for (const auto& address : m_address_book) {
- for (const auto& data : address.second.destdata) {
- if (!data.first.compare(0, prefix.size(), prefix)) {
- values.emplace_back(data.second);
- }
+ for (const auto& [dest, entry] : m_address_book) {
+ for (const auto& [id, request] : entry.receive_requests) {
+ values.emplace_back(request);
}
}
return values;
@@ -2869,15 +2829,15 @@ std::vector<std::string> CWallet::GetAddressReceiveRequests() const
bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value)
{
- const std::string key{"rr" + id}; // "rr" prefix = "receive request" in destdata
- CAddressBookData& data = m_address_book.at(dest);
- if (value.empty()) {
- if (!batch.EraseDestData(EncodeDestination(dest), key)) return false;
- data.destdata.erase(key);
- } else {
- if (!batch.WriteDestData(EncodeDestination(dest), key, value)) return false;
- data.destdata[key] = value;
- }
+ if (!batch.WriteAddressReceiveRequest(dest, id, value)) return false;
+ m_address_book[dest].receive_requests[id] = value;
+ return true;
+}
+
+bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id)
+{
+ if (!batch.EraseAddressReceiveRequest(dest, id)) return false;
+ m_address_book[dest].receive_requests.erase(id);
return true;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 581a6bd9cb..5252b46cdc 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -221,17 +221,22 @@ struct CAddressBookData
std::optional<AddressPurpose> purpose;
/**
- * Additional address metadata map that can currently hold two types of keys:
- *
- * "used" keys with values always set to "1" or "p" if present. This is set on
- * IsMine addresses that have already been spent from if the
- * avoid_reuse option is enabled
- *
- * "rr##" keys where ## is a decimal number, with serialized
- * RecentRequestEntry objects as values
+ * Whether coins with this address have previously been spent. Set when the
+ * the wallet avoid_reuse option is enabled and this is an IsMine address
+ * that has already received funds and spent them. This is used during coin
+ * selection to increase privacy by not creating different transactions
+ * that spend from the same addresses.
+ */
+ bool previously_spent{false};
+
+ /**
+ * Map containing data about previously generated receive requests
+ * requesting funds to be sent to this address. Only present for IsMine
+ * addresses. Map keys are decimal numbers uniquely identifying each
+ * request, and map values are serialized RecentRequestEntry objects
+ * containing BIP21 URI information including message and amount.
*/
- typedef std::map<std::string, std::string> StringMap;
- StringMap destdata;
+ std::map<std::string, std::string> receive_requests{};
/** Accessor methods. */
bool IsChange() const { return !label.has_value(); }
@@ -511,8 +516,10 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; }
- //! Adds a destination data tuple to the store, without saving it to disk
- void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Marks destination as previously spent.
+ void LoadAddressPreviouslySpent(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Appends payment request to destination.
+ void LoadAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& request) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime GUARDED_BY(cs_wallet){0};
@@ -739,11 +746,12 @@ public:
bool DelAddressBook(const CTxDestination& address);
- bool IsAddressUsed(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsAddressPreviouslySpent(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<std::string> GetAddressReceiveRequests() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index c8e8ce4614..005592d720 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -615,7 +615,20 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
ssKey >> strAddress;
ssKey >> strKey;
ssValue >> strValue;
- pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue);
+ const CTxDestination& dest{DecodeDestination(strAddress)};
+ if (strKey.compare("used") == 0) {
+ // Load "used" key indicating if an IsMine address has
+ // previously been spent from with avoid_reuse option enabled.
+ // The strValue is not used for anything currently, but could
+ // hold more information in the future. Current values are just
+ // "1" or "p" for present (which was written prior to
+ // f5ba424cd44619d9b9be88b8593d69a7ba96db26).
+ pwallet->LoadAddressPreviouslySpent(dest);
+ } else if (strKey.compare(0, 2, "rr") == 0) {
+ // Load "rr##" keys where ## is a decimal number, and strValue
+ // is a serialized RecentRequestEntry object.
+ pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue);
+ }
} else if (strType == DBKeys::HDCHAIN) {
CHDChain chain;
ssValue >> chain;
@@ -1088,16 +1101,28 @@ void MaybeCompactWalletDB(WalletContext& context)
fOneThread = false;
}
-bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
+bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
+{
+ auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
+ return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
+}
+
+bool WalletBatch::WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request)
{
- return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)), value);
+ return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)), receive_request);
}
-bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
+bool WalletBatch::EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id)
{
- return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)));
+ return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)));
}
+bool WalletBatch::EraseAddressData(const CTxDestination& dest)
+{
+ DataStream prefix;
+ prefix << DBKeys::DESTDATA << EncodeDestination(dest);
+ return m_batch->ErasePrefix(prefix);
+}
bool WalletBatch::WriteHDChain(const CHDChain& chain)
{
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index c97356a71f..72086e950a 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLETDB_H
#include <script/sign.h>
+#include <script/standard.h>
#include <wallet/db.h>
#include <wallet/walletutil.h>
#include <key.h>
@@ -264,10 +265,10 @@ public:
bool WriteLockedUTXO(const COutPoint& output);
bool EraseLockedUTXO(const COutPoint& output);
- /// Write destination data key,value tuple to database
- bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
- /// Erase destination data tuple from wallet database
- bool EraseDestData(const std::string &address, const std::string &key);
+ bool WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent);
+ bool WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request);
+ bool EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id);
+ bool EraseAddressData(const CTxDestination& dest);
bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal);
bool EraseActiveScriptPubKeyMan(uint8_t type, bool internal);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index acd817a311..2f3f8ef77d 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -8,8 +8,8 @@
#include <wallet/wallettool.h>
+#include <common/args.h>
#include <util/fs.h>
-#include <util/system.h>
#include <util/translation.h>
#include <wallet/dump.h>
#include <wallet/salvage.h>
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 299c74d01c..fdd5bc36d8 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -4,8 +4,8 @@
#include <wallet/walletutil.h>
+#include <common/args.h>
#include <logging.h>
-#include <util/system.h>
namespace wallet {
fs::path GetWalletDir()
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index df129c0830..9920d80a69 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -4,10 +4,10 @@
#include <zmq/zmqnotificationinterface.h>
+#include <common/args.h>
#include <logging.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <util/system.h>
#include <validationinterface.h>
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqpublishnotifier.h>