aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.test.include5
-rw-r--r--src/addrdb.cpp70
-rw-r--r--src/addrdb.h45
-rw-r--r--src/addrman.cpp38
-rw-r--r--src/addrman.h50
-rw-r--r--src/bench/addrman.cpp12
-rw-r--r--src/bench/coin_selection.cpp13
-rw-r--r--src/bench/wallet_balance.cpp5
-rw-r--r--src/bitcoin-cli.cpp46
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/httprpc.cpp6
-rw-r--r--src/httpserver.cpp4
-rw-r--r--src/init.cpp67
-rw-r--r--src/key.cpp1
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/logging/timer.h26
-rw-r--r--src/net.cpp19
-rw-r--r--src/net.h6
-rw-r--r--src/net_types.cpp65
-rw-r--r--src/net_types.h47
-rw-r--r--src/netaddress.cpp5
-rw-r--r--src/netaddress.h2
-rw-r--r--src/pubkey.cpp18
-rw-r--r--src/pubkey.h5
-rw-r--r--src/qt/bantablemodel.h1
-rw-r--r--src/qt/bitcoingui.cpp4
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/walletframe.cpp10
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletview.cpp82
-rw-r--r--src/qt/walletview.h16
-rw-r--r--src/rpc/mining.cpp3
-rw-r--r--src/rpc/misc.cpp12
-rw-r--r--src/script/descriptor.cpp8
-rw-r--r--src/script/script.h9
-rw-r--r--src/script/sign.cpp17
-rw-r--r--src/script/signingprovider.h24
-rw-r--r--src/signet.cpp2
-rw-r--r--src/sync.cpp11
-rw-r--r--src/sync.h20
-rw-r--r--src/test/addrman_tests.cpp34
-rw-r--r--src/test/bip32_tests.cpp34
-rw-r--r--src/test/fuzz/addrdb.cpp23
-rw-r--r--src/test/fuzz/addrman.cpp28
-rw-r--r--src/test/fuzz/asmap.cpp3
-rw-r--r--src/test/fuzz/connman.cpp8
-rw-r--r--src/test/fuzz/data_stream.cpp5
-rw-r--r--src/test/fuzz/deserialize.cpp2
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/net.cpp7
-rw-r--r--src/test/fuzz/versionbits.cpp1
-rw-r--r--src/test/logging_tests.cpp8
-rw-r--r--src/test/transaction_tests.cpp129
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util_tests.cpp16
-rw-r--r--src/util/asmap.cpp38
-rw-r--r--src/util/asmap.h7
-rw-r--r--src/util/getuniquepath.cpp4
-rw-r--r--src/util/settings.cpp8
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/util/types.h11
-rw-r--r--src/validation.cpp2
-rw-r--r--src/validation.h2
-rw-r--r--src/wallet/feebumper.cpp14
-rw-r--r--src/wallet/interfaces.cpp34
-rw-r--r--src/wallet/load.cpp3
-rw-r--r--src/wallet/receive.cpp229
-rw-r--r--src/wallet/receive.h44
-rw-r--r--src/wallet/rpcwallet.cpp63
-rw-r--r--src/wallet/spend.cpp191
-rw-r--r--src/wallet/spend.h83
-rw-r--r--src/wallet/test/coinselector_tests.cpp15
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp4
-rw-r--r--src/wallet/test/spend_tests.cpp3
-rw-r--r--src/wallet/test/wallet_tests.cpp38
-rw-r--r--src/wallet/transaction.h73
-rw-r--r--src/wallet/wallet.cpp71
-rw-r--r--src/wallet/wallet.h120
-rw-r--r--src/wallet/walletdb.cpp2
-rw-r--r--src/zmq/zmqpublishnotifier.cpp24
84 files changed, 1151 insertions, 1025 deletions
diff --git a/src/.clang-tidy b/src/.clang-tidy
new file mode 100644
index 0000000000..27616ad072
--- /dev/null
+++ b/src/.clang-tidy
@@ -0,0 +1,2 @@
+Checks: '-*,bugprone-argument-comment'
+WarningsAsErrors: bugprone-argument-comment
diff --git a/src/Makefile.am b/src/Makefile.am
index a8d6591e98..eea98c7f22 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -268,6 +268,7 @@ BITCOIN_CORE_H = \
util/tokenpipe.h \
util/trace.h \
util/translation.h \
+ util/types.h \
util/ui_change_type.h \
util/url.h \
util/vector.h \
@@ -548,6 +549,7 @@ libbitcoin_common_a_SOURCES = \
key.cpp \
key_io.cpp \
merkleblock.cpp \
+ net_types.cpp \
netaddress.cpp \
netbase.cpp \
net_permissions.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 40d44aaa2e..a85a359960 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -207,7 +207,6 @@ test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_fuzz_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) $(RUNTIME_LDFLAGS)
test_fuzz_fuzz_SOURCES = \
test/fuzz/addition_overflow.cpp \
- test/fuzz/addrdb.cpp \
test/fuzz/addrman.cpp \
test/fuzz/asmap.cpp \
test/fuzz/asmap_direct.cpp \
@@ -338,8 +337,8 @@ bitcoin_test_clean : FORCE
check-local: $(BITCOIN_TESTS:.cpp=.cpp.test)
if BUILD_BITCOIN_TX
- @echo "Running test/util/bitcoin-util-test.py..."
- $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py
+ @echo "Running test/util/test_runner.py..."
+ $(PYTHON) $(top_builddir)/test/util/test_runner.py
endif
@echo "Running test/util/rpcauth-test.py..."
$(PYTHON) $(top_builddir)/test/util/rpcauth-test.py
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index a5383be7cf..856f318961 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -19,64 +19,7 @@
#include <util/settings.h>
#include <util/system.h>
-CBanEntry::CBanEntry(const UniValue& json)
- : nVersion(json["version"].get_int()), nCreateTime(json["ban_created"].get_int64()),
- nBanUntil(json["banned_until"].get_int64())
-{
-}
-
-UniValue CBanEntry::ToJson() const
-{
- UniValue json(UniValue::VOBJ);
- json.pushKV("version", nVersion);
- json.pushKV("ban_created", nCreateTime);
- json.pushKV("banned_until", nBanUntil);
- return json;
-}
-
namespace {
-
-static const char* BANMAN_JSON_ADDR_KEY = "address";
-
-/**
- * Convert a `banmap_t` object to a JSON array.
- * @param[in] bans Bans list to convert.
- * @return a JSON array, similar to the one returned by the `listbanned` RPC. Suitable for
- * passing to `BanMapFromJson()`.
- */
-UniValue BanMapToJson(const banmap_t& bans)
-{
- UniValue bans_json(UniValue::VARR);
- for (const auto& it : bans) {
- const auto& address = it.first;
- const auto& ban_entry = it.second;
- UniValue j = ban_entry.ToJson();
- j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
- bans_json.push_back(j);
- }
- return bans_json;
-}
-
-/**
- * Convert a JSON array to a `banmap_t` object.
- * @param[in] bans_json JSON to convert, must be as returned by `BanMapToJson()`.
- * @param[out] bans Bans list to create from the JSON.
- * @throws std::runtime_error if the JSON does not have the expected fields or they contain
- * unparsable values.
- */
-void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
-{
- for (const auto& ban_entry_json : bans_json.getValues()) {
- CSubNet subnet;
- const auto& subnet_str = ban_entry_json[BANMAN_JSON_ADDR_KEY].get_str();
- if (!LookupSubNet(subnet_str, subnet)) {
- throw std::runtime_error(
- strprintf("Cannot parse banned address or subnet: %s", subnet_str));
- }
- bans.insert_or_assign(subnet, CBanEntry{ban_entry_json});
- }
-}
-
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
@@ -227,22 +170,19 @@ bool CBanDB::Read(banmap_t& banSet)
return true;
}
-CAddrDB::CAddrDB()
-{
- pathAddr = gArgs.GetDataDirNet() / "peers.dat";
-}
-
-bool CAddrDB::Write(const CAddrMan& addr)
+bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
{
+ const auto pathAddr = args.GetDataDirNet() / "peers.dat";
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
-bool CAddrDB::Read(CAddrMan& addr)
+bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr)
{
+ const auto pathAddr = args.GetDataDirNet() / "peers.dat";
return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
}
-bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
+bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
{
return DeserializeDB(ssPeers, addr, false);
}
diff --git a/src/addrdb.h b/src/addrdb.h
index 26400ee0b6..c31c126ee3 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -12,48 +12,15 @@
#include <vector>
-class CAddress;
+class ArgsManager;
class CAddrMan;
+class CAddress;
class CDataStream;
-class CBanEntry
-{
-public:
- static constexpr int CURRENT_VERSION{1};
- int nVersion{CBanEntry::CURRENT_VERSION};
- int64_t nCreateTime{0};
- int64_t nBanUntil{0};
-
- CBanEntry() {}
-
- explicit CBanEntry(int64_t nCreateTimeIn)
- : nCreateTime{nCreateTimeIn} {}
-
- /**
- * Create a ban entry from JSON.
- * @param[in] json A JSON representation of a ban entry, as created by `ToJson()`.
- * @throw std::runtime_error if the JSON does not have the expected fields.
- */
- explicit CBanEntry(const UniValue& json);
-
- /**
- * Generate a JSON representation of this ban entry.
- * @return JSON suitable for passing to the `CBanEntry(const UniValue&)` constructor.
- */
- UniValue ToJson() const;
-};
-
-/** Access to the (IP) address database (peers.dat) */
-class CAddrDB
-{
-private:
- fs::path pathAddr;
-public:
- CAddrDB();
- bool Write(const CAddrMan& addr);
- bool Read(CAddrMan& addr);
- static bool Read(CAddrMan& addr, CDataStream& ssPeers);
-};
+bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
+bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr);
+/** Only used by tests. */
+bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
/** Access to the banlist database (banlist.json) */
class CBanDB
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 48e79c64ed..772c34ae77 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -5,10 +5,12 @@
#include <addrman.h>
+#include <clientversion.h>
#include <hash.h>
#include <logging.h>
#include <netaddress.h>
#include <serialize.h>
+#include <streams.h>
#include <cmath>
#include <optional>
@@ -98,10 +100,11 @@ double CAddrInfo::GetChance(int64_t nNow) const
return fChance;
}
-CAddrMan::CAddrMan(bool deterministic, int32_t consistency_check_ratio)
+CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
: insecure_rand{deterministic}
, nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
, m_consistency_check_ratio{consistency_check_ratio}
+ , m_asmap{std::move(asmap)}
{
for (auto& bucket : vvNew) {
for (auto& entry : bucket) {
@@ -242,9 +245,9 @@ void CAddrMan::Unserialize(Stream& s_)
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
if (lowest_compatible > FILE_FORMAT) {
throw std::ios_base::failure(strprintf(
- "Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
- "but the maximum supported by this version of %s is %u.",
- format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
+ "Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
+ "but the maximum supported by this version of %s is %u.",
+ uint8_t{format}, uint8_t{lowest_compatible}, PACKAGE_NAME, uint8_t{FILE_FORMAT}));
}
s >> nKey;
@@ -1006,30 +1009,3 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
return mapInfo[id_old];
}
-
-std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
-{
- std::vector<bool> bits;
- FILE *filestr = fsbridge::fopen(path, "rb");
- CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
- if (file.IsNull()) {
- LogPrintf("Failed to open asmap file from disk\n");
- return bits;
- }
- fseek(filestr, 0, SEEK_END);
- int length = ftell(filestr);
- LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
- fseek(filestr, 0, SEEK_SET);
- uint8_t cur_byte;
- for (int i = 0; i < length; ++i) {
- file >> cur_byte;
- for (int bit = 0; bit < 8; ++bit) {
- bits.push_back((cur_byte >> bit) & 1);
- }
- }
- if (!SanityCheckASMap(bits)) {
- LogPrintf("Sanity check of asmap file %s failed\n", path);
- return {};
- }
- return bits;
-}
diff --git a/src/addrman.h b/src/addrman.h
index 2548b891ba..0885231ebc 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,23 +6,16 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
-#include <clientversion.h>
-#include <config/bitcoin-config.h>
#include <fs.h>
-#include <hash.h>
+#include <logging.h>
#include <netaddress.h>
#include <protocol.h>
-#include <random.h>
-#include <streams.h>
#include <sync.h>
#include <timedata.h>
-#include <tinyformat.h>
-#include <util/system.h>
-#include <iostream>
+#include <cstdint>
#include <optional>
#include <set>
-#include <stdint.h>
#include <unordered_map>
#include <vector>
@@ -149,32 +142,13 @@ static constexpr int ADDRMAN_BUCKET_SIZE{1 << ADDRMAN_BUCKET_SIZE_LOG2};
class CAddrMan
{
public:
- // Compressed IP->ASN mapping, loaded from a file when a node starts.
- // Should be always empty if no file was provided.
- // This mapping is then used for bucketing nodes in Addrman.
- //
- // If asmap is provided, nodes will be bucketed by
- // AS they belong to, in order to make impossible for a node
- // to connect to several nodes hosted in a single AS.
- // This is done in response to Erebus attack, but also to generally
- // diversify the connections every node creates,
- // especially useful when a large fraction of nodes
- // operate under a couple of cloud providers.
- //
- // If a new asmap was provided, the existing records
- // would be re-bucketed accordingly.
- std::vector<bool> m_asmap;
-
- // Read asmap from provided binary file
- static std::vector<bool> DecodeAsmap(fs::path path);
-
template <typename Stream>
void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
template <typename Stream>
void Unserialize(Stream& s_) EXCLUSIVE_LOCKS_REQUIRED(!cs);
- explicit CAddrMan(bool deterministic, int32_t consistency_check_ratio);
+ explicit CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio);
~CAddrMan()
{
@@ -296,6 +270,8 @@ public:
Check();
}
+ const std::vector<bool>& GetAsmap() const { return m_asmap; }
+
private:
//! A mutex to protect the inner data structures.
mutable Mutex cs;
@@ -363,6 +339,22 @@ private:
/** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
const int32_t m_consistency_check_ratio;
+ // Compressed IP->ASN mapping, loaded from a file when a node starts.
+ // Should be always empty if no file was provided.
+ // This mapping is then used for bucketing nodes in Addrman.
+ //
+ // If asmap is provided, nodes will be bucketed by
+ // AS they belong to, in order to make impossible for a node
+ // to connect to several nodes hosted in a single AS.
+ // This is done in response to Erebus attack, but also to generally
+ // diversify the connections every node creates,
+ // especially useful when a large fraction of nodes
+ // operate under a couple of cloud providers.
+ //
+ // If a new asmap was provided, the existing records
+ // would be re-bucketed accordingly.
+ const std::vector<bool> m_asmap;
+
//! Find an entry.
CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index d69a651811..e5dd571a4c 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -5,6 +5,7 @@
#include <addrman.h>
#include <bench/bench.h>
#include <random.h>
+#include <util/check.h>
#include <util/time.h>
#include <optional>
@@ -73,14 +74,14 @@ static void AddrManAdd(benchmark::Bench& bench)
CreateAddresses();
bench.run([&] {
- CAddrMan addrman{/* deterministic */ false, /* consistency_check_ratio */ 0};
+ CAddrMan addrman{/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0};
AddAddressesToAddrMan(addrman);
});
}
static void AddrManSelect(benchmark::Bench& bench)
{
- CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
FillAddrMan(addrman);
@@ -92,7 +93,7 @@ static void AddrManSelect(benchmark::Bench& bench)
static void AddrManGetAddr(benchmark::Bench& bench)
{
- CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
FillAddrMan(addrman);
@@ -110,11 +111,12 @@ static void AddrManGood(benchmark::Bench& bench)
* we want to do the same amount of work in every loop iteration. */
bench.epochs(5).epochIterations(1);
- const size_t addrman_count{bench.epochs() * bench.epochIterations()};
+ const uint64_t addrman_count{bench.epochs() * bench.epochIterations()};
+ Assert(addrman_count == 5U);
std::vector<std::unique_ptr<CAddrMan>> addrmans(addrman_count);
for (size_t i{0}; i < addrman_count; ++i) {
- addrmans[i] = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ addrmans[i] = std::make_unique<CAddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
FillAddrMan(*addrmans[i]);
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 5beb833b48..934b574f8b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -6,6 +6,7 @@
#include <interfaces/chain.h>
#include <node/context.h>
#include <wallet/coinselection.h>
+#include <wallet/spend.h>
#include <wallet/wallet.h>
#include <set>
@@ -17,7 +18,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(1);
tx.vout[0].nValue = nValue;
- wtxs.push_back(std::make_unique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
+ wtxs.push_back(std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx))));
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -45,18 +46,18 @@ static void CoinSelection(benchmark::Bench& bench)
// Create coins
std::vector<COutput> coins;
for (const auto& wtx : wtxs) {
- coins.emplace_back(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
+ coins.emplace_back(wallet, *wtx, 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(/* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
- bool success = wallet.AttemptSelection(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
+ bool success = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
@@ -75,9 +76,9 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
+ std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx)));
set.emplace_back();
- set.back().Insert(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
+ set.back().Insert(COutput(testWallet, *wtx, nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
wtxn.emplace_back(std::move(wtx));
}
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index 362b7c1e15..a205d8b6e7 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -9,6 +9,7 @@
#include <test/util/setup_common.h>
#include <test/util/wallet.h>
#include <validationinterface.h>
+#include <wallet/receive.h>
#include <wallet/wallet.h>
#include <optional>
@@ -35,11 +36,11 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
}
SyncWithValidationInterfaceQueue();
- auto bal = wallet.GetBalance(); // Cache
+ auto bal = GetBalance(wallet); // Cache
bench.run([&] {
if (set_dirty) wallet.MarkDirty();
- bal = wallet.GetBalance();
+ bal = GetBalance(wallet);
if (add_mine) assert(bal.m_mine_trusted > 0);
if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
});
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 34785bf6a4..e75ba81b54 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -93,9 +93,6 @@ static void SetupCliArgs(ArgsManager& argsman)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
-#ifndef EVENT_LOG_ERR // EVENT_LOG_ERR was added in 2.0.19; but before then _EVENT_LOG_ERR existed.
-# define EVENT_LOG_ERR _EVENT_LOG_ERR
-#endif
// Ignore everything other than errors
if (severity >= EVENT_LOG_ERR) {
throw std::runtime_error(strprintf("libevent error: %s", msg));
@@ -386,7 +383,9 @@ private:
bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
bool m_is_asmap_on{false};
size_t m_max_addr_length{0};
- size_t m_max_age_length{3};
+ size_t m_max_addr_processed_length{5};
+ size_t m_max_addr_rate_limited_length{6};
+ size_t m_max_age_length{5};
size_t m_max_id_length{2};
struct Peer {
std::string addr;
@@ -396,6 +395,8 @@ private:
std::string age;
double min_ping;
double ping;
+ int64_t addr_processed;
+ int64_t addr_rate_limited;
int64_t last_blck;
int64_t last_recv;
int64_t last_send;
@@ -403,6 +404,7 @@ private:
int id;
int mapped_as;
int version;
+ bool is_addr_relay_enabled;
bool is_bip152_hb_from;
bool is_bip152_hb_to;
bool is_block_relay;
@@ -483,6 +485,8 @@ public:
const int peer_id{peer["id"].get_int()};
const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
const int version{peer["version"].get_int()};
+ const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].get_int64()};
+ const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].get_int64()};
const int64_t conn_time{peer["conntime"].get_int64()};
const int64_t last_blck{peer["last_block"].get_int64()};
const int64_t last_recv{peer["lastrecv"].get_int64()};
@@ -493,10 +497,13 @@ public:
const std::string addr{peer["addr"].get_str()};
const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)};
const std::string sub_version{peer["subver"].get_str()};
+ const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
- m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
+ m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
+ m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
m_max_age_length = std::max(age.length(), m_max_age_length);
m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
m_is_asmap_on |= (mapped_as != 0);
@@ -504,39 +511,46 @@ public:
}
// Generate report header.
- std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
+ std::string result{strprintf("%s client %s%s - server %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
// Report detailed peer connections list sorted by direction and minimum ping time.
if (DetailsRequested() && !m_peers.empty()) {
std::sort(m_peers.begin(), m_peers.end());
- result += strprintf("<-> type net mping ping send recv txn blk hb %*s ", m_max_age_length, "age");
+ result += strprintf("<-> type net mping ping send recv txn blk hb %*s%*s%*s ",
+ m_max_addr_processed_length, "addrp",
+ m_max_addr_rate_limited_length, "addrl",
+ m_max_age_length, "age");
if (m_is_asmap_on) result += " asmap ";
result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
for (const Peer& peer : m_peers) {
std::string version{ToString(peer.version) + peer.sub_version};
result += strprintf(
- "%3s %6s %5s%7s%7s%5s%5s%5s%5s %2s %*s%*i %*s %-*s%s\n",
+ "%3s %6s %5s%7s%7s%5s%5s%5s%5s %2s %*s%*s%*s%*i %*s %-*s%s\n",
peer.is_outbound ? "out" : "in",
ConnectionTypeForNetinfo(peer.conn_type),
peer.network,
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
- peer.last_send == 0 ? "" : ToString(m_time_now - peer.last_send),
- peer.last_recv == 0 ? "" : ToString(m_time_now - peer.last_recv),
- peer.last_trxn == 0 ? "" : ToString((m_time_now - peer.last_trxn) / 60),
- peer.last_blck == 0 ? "" : ToString((m_time_now - peer.last_blck) / 60),
+ peer.last_send ? ToString(m_time_now - peer.last_send) : "",
+ peer.last_recv ? ToString(m_time_now - peer.last_recv) : "",
+ peer.last_trxn ? ToString((m_time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "",
+ peer.last_blck ? ToString((m_time_now - peer.last_blck) / 60) : "",
strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
+ m_max_addr_processed_length, // variable spacing
+ peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
+ m_max_addr_rate_limited_length, // variable spacing
+ peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "",
m_max_age_length, // variable spacing
peer.age,
m_is_asmap_on ? 7 : 0, // variable spacing
- m_is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "",
+ m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "",
m_max_id_length, // variable spacing
peer.id,
IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
IsAddressSelected() ? peer.addr : "",
IsVersionSelected() && version != "0" ? version : "");
}
- result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
+ result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
}
// Report peer connection totals by type.
@@ -610,10 +624,14 @@ public:
" send Time since last message sent to the peer, in seconds\n"
" recv Time since last message received from the peer, in seconds\n"
" txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
+ " \"*\" - the peer requested we not relay transactions to it (relaytxes is false)\n"
" blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
" hb High-bandwidth BIP152 compact block relay\n"
" \".\" (to) - we selected the peer as a high-bandwidth peer\n"
" \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
+ " addrp Total number of addresses processed, excluding those dropped due to rate limiting\n"
+ " \".\" - we do not relay addresses to this peer (addr_relay_enabled is false)\n"
+ " addrl Total number of addresses dropped due to rate limiting\n"
" age Duration of connection to the peer, in minutes\n"
" asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
" peer selection (only displayed if the -asmap config option is set)\n"
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index cd2cdf01f2..58c51bd8e0 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -772,9 +772,7 @@ static std::string readStdin()
if (ferror(stdin))
throw std::runtime_error("error reading stdin");
- boost::algorithm::trim_right(ret);
-
- return ret;
+ return TrimString(ret);
}
static int CommandLineRawTx(int argc, char* argv[])
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index e11e4acb5c..9ae592be79 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -10,6 +10,7 @@
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
#include <walletinitinterface.h>
@@ -22,7 +23,7 @@
#include <set>
#include <string>
-#include <boost/algorithm/string.hpp> // boost::trim
+#include <boost/algorithm/string.hpp>
/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
@@ -130,8 +131,7 @@ static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUserna
return false;
if (strAuth.substr(0, 6) != "Basic ")
return false;
- std::string strUserPass64 = strAuth.substr(6);
- boost::trim(strUserPass64);
+ std::string strUserPass64 = TrimString(strAuth.substr(6));
std::string strUserPass = DecodeBase64(strUserPass64);
if (strUserPass.find(':') != std::string::npos)
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 8741ad9b86..fa0379f612 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -338,10 +338,6 @@ static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
-#ifndef EVENT_LOG_WARN
-// EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
-# define EVENT_LOG_WARN _EVENT_LOG_WARN
-#endif
if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
LogPrintf("libevent: %s\n", msg);
else
diff --git a/src/init.cpp b/src/init.cpp
index d4a3c891b1..2753745843 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1171,23 +1171,51 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
fDiscover = args.GetBoolArg("-discover", true);
const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)};
- assert(!node.addrman);
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- node.addrman = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ check_addrman);
{
+ // Initialize addrman
+ assert(!node.addrman);
+
+ // Read asmap file if configured
+ std::vector<bool> asmap;
+ if (args.IsArgSet("-asmap")) {
+ fs::path asmap_path = fs::path(args.GetArg("-asmap", ""));
+ if (asmap_path.empty()) {
+ asmap_path = DEFAULT_ASMAP_FILENAME;
+ }
+ if (!asmap_path.is_absolute()) {
+ asmap_path = gArgs.GetDataDirNet() / asmap_path;
+ }
+ if (!fs::exists(asmap_path)) {
+ InitError(strprintf(_("Could not find asmap file %s"), asmap_path));
+ return false;
+ }
+ asmap = DecodeAsmap(asmap_path);
+ if (asmap.size() == 0) {
+ InitError(strprintf(_("Could not parse asmap file %s"), asmap_path));
+ return false;
+ }
+ const uint256 asmap_version = SerializeHash(asmap);
+ LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString());
+ } else {
+ LogPrintf("Using /16 prefix for IP bucketing\n");
+ }
+
+ auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+
// Load addresses from peers.dat
uiInterface.InitMessage(_("Loading P2P addresses…").translated);
int64_t nStart = GetTimeMillis();
- CAddrDB adb;
- if (adb.Read(*node.addrman)) {
+ if (ReadPeerAddresses(args, *node.addrman)) {
LogPrintf("Loaded %i addresses from peers.dat %dms\n", node.addrman->size(), GetTimeMillis() - nStart);
} else {
// Addrman can be in an inconsistent state after failure, reset it
- node.addrman = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
LogPrintf("Recreating peers.dat\n");
- adb.Write(*node.addrman);
+ DumpPeerAddresses(args, *node.addrman);
}
}
+
assert(!node.banman);
node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
@@ -1292,31 +1320,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
return InitError(ResolveErrMsg("externalip", strAddr));
}
- // Read asmap file if configured
- if (args.IsArgSet("-asmap")) {
- fs::path asmap_path = fs::path(args.GetArg("-asmap", ""));
- if (asmap_path.empty()) {
- asmap_path = DEFAULT_ASMAP_FILENAME;
- }
- if (!asmap_path.is_absolute()) {
- asmap_path = gArgs.GetDataDirNet() / asmap_path;
- }
- if (!fs::exists(asmap_path)) {
- InitError(strprintf(_("Could not find asmap file %s"), asmap_path));
- return false;
- }
- std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
- if (asmap.size() == 0) {
- InitError(strprintf(_("Could not parse asmap file %s"), asmap_path));
- return false;
- }
- const uint256 asmap_version = SerializeHash(asmap);
- node.connman->SetAsmap(std::move(asmap));
- LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString());
- } else {
- LogPrintf("Using /16 prefix for IP bucketing\n");
- }
-
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create();
diff --git a/src/key.cpp b/src/key.cpp
index 7bef3d529b..40df248e02 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -357,6 +357,7 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
memcpy(chaincode.begin(), code+9, 32);
key.Set(code+42, code+BIP32_EXTKEY_SIZE, true);
+ if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey();
}
bool ECC_InitSanityCheck() {
diff --git a/src/logging.cpp b/src/logging.cpp
index b456108b61..eb2c750296 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -159,6 +159,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::VALIDATION, "validation"},
{BCLog::I2P, "i2p"},
{BCLog::IPC, "ipc"},
+ {BCLog::LOCK, "lock"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
diff --git a/src/logging.h b/src/logging.h
index 38d73863e7..53a89d28bd 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -59,6 +59,7 @@ namespace BCLog {
VALIDATION = (1 << 21),
I2P = (1 << 22),
IPC = (1 << 23),
+ LOCK = (1 << 24),
ALL = ~(uint32_t)0,
};
diff --git a/src/logging/timer.h b/src/logging/timer.h
index 159920e397..79627b1fe3 100644
--- a/src/logging/timer.h
+++ b/src/logging/timer.h
@@ -9,6 +9,7 @@
#include <logging.h>
#include <util/macros.h>
#include <util/time.h>
+#include <util/types.h>
#include <chrono>
#include <string>
@@ -58,21 +59,15 @@ public:
return strprintf("%s: %s", m_prefix, msg);
}
- std::string units = "";
- float divisor = 1;
-
- if (std::is_same<TimeType, std::chrono::microseconds>::value) {
- units = "μs";
- } else if (std::is_same<TimeType, std::chrono::milliseconds>::value) {
- units = "ms";
- divisor = 1000.;
- } else if (std::is_same<TimeType, std::chrono::seconds>::value) {
- units = "s";
- divisor = 1000. * 1000.;
+ if constexpr (std::is_same<TimeType, std::chrono::microseconds>::value) {
+ return strprintf("%s: %s (%iμs)", m_prefix, msg, end_time.count());
+ } else if constexpr (std::is_same<TimeType, std::chrono::milliseconds>::value) {
+ return strprintf("%s: %s (%.2fms)", m_prefix, msg, end_time.count() * 0.001);
+ } else if constexpr (std::is_same<TimeType, std::chrono::seconds>::value) {
+ return strprintf("%s: %s (%.2fs)", m_prefix, msg, end_time.count() * 0.000001);
+ } else {
+ static_assert(ALWAYS_FALSE<TimeType>, "Error: unexpected time type");
}
-
- const float time_ms = end_time.count() / divisor;
- return strprintf("%s: %s (%.2f%s)", m_prefix, msg, time_ms, units);
}
private:
@@ -87,12 +82,13 @@ private:
//! Forwarded on to LogPrint if specified - has the effect of only
//! outputting the timing log when a particular debug= category is specified.
const BCLog::LogFlags m_log_category{};
-
};
} // namespace BCLog
+#define LOG_TIME_MICROS_WITH_CATEGORY(end_msg, log_category) \
+ BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
#define LOG_TIME_MILLIS_WITH_CATEGORY(end_msg, log_category) \
BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
#define LOG_TIME_SECONDS(end_msg) \
diff --git a/src/net.cpp b/src/net.cpp
index 9b1e17c587..c72cd75ba7 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -9,6 +9,7 @@
#include <net.h>
+#include <addrdb.h>
#include <banman.h>
#include <clientversion.h>
#include <compat.h>
@@ -24,6 +25,7 @@
#include <scheduler.h>
#include <util/sock.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <util/thread.h>
#include <util/trace.h>
#include <util/translation.h>
@@ -552,14 +554,13 @@ Network CNode::ConnectedThroughNetwork() const
#undef X
#define X(name) stats.name = name
-void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
+void CNode::CopyStats(CNodeStats& stats)
{
stats.nodeid = this->GetId();
X(nServices);
X(addr);
X(addrBind);
stats.m_network = ConnectedThroughNetwork();
- stats.m_mapped_as = addr.GetMappedAS(m_asmap);
if (m_tx_relay != nullptr) {
LOCK(m_tx_relay->cs_filter);
stats.fRelayTxes = m_tx_relay->fRelayTxes;
@@ -1747,8 +1748,7 @@ void CConnman::DumpAddresses()
{
int64_t nStart = GetTimeMillis();
- CAddrDB adb;
- adb.Write(addrman);
+ DumpPeerAddresses(::gArgs, addrman);
LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart);
@@ -1921,7 +1921,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
- setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
+ setConnected.insert(pnode->addr.GetGroup(addrman.GetAsmap()));
} // no default case, so the compiler can warn about missing cases
}
}
@@ -1995,7 +1995,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
m_anchors.pop_back();
if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
!HasAllDesirableServiceFlags(addr.nServices) ||
- setConnected.count(addr.GetGroup(addrman.m_asmap))) continue;
+ setConnected.count(addr.GetGroup(addrman.GetAsmap()))) continue;
addrConnect = addr;
LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
break;
@@ -2035,7 +2035,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Require outbound connections, other than feelers, to be to distinct network groups
- if (!fFeeler && setConnected.count(addr.GetGroup(addrman.m_asmap))) {
+ if (!fFeeler && setConnected.count(addr.GetGroup(addrman.GetAsmap()))) {
break;
}
@@ -2804,7 +2804,8 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
vstats.reserve(vNodes.size());
for (CNode* pnode : vNodes) {
vstats.emplace_back();
- pnode->copyStats(vstats.back(), addrman.m_asmap);
+ pnode->CopyStats(vstats.back());
+ vstats.back().m_mapped_as = pnode->addr.GetMappedAS(addrman.GetAsmap());
}
}
@@ -3067,7 +3068,7 @@ CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const
{
- std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.m_asmap));
+ std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.GetAsmap()));
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
}
diff --git a/src/net.h b/src/net.h
index d568c56205..0a72ca888d 100644
--- a/src/net.h
+++ b/src/net.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_NET_H
#define BITCOIN_NET_H
-#include <addrdb.h>
#include <addrman.h>
#include <amount.h>
#include <bloom.h>
@@ -652,7 +651,7 @@ public:
void CloseSocketDisconnect();
- void copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap);
+ void CopyStats(CNodeStats& stats);
ServiceFlags GetLocalServices() const
{
@@ -768,7 +767,6 @@ public:
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
- std::vector<bool> m_asmap;
bool m_i2p_accept_incoming;
};
@@ -943,8 +941,6 @@ public:
*/
std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
- void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
-
/** Return true if we should disconnect the peer for failing an inactivity check. */
bool ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now=std::nullopt) const;
diff --git a/src/net_types.cpp b/src/net_types.cpp
new file mode 100644
index 0000000000..c8f57fe6c6
--- /dev/null
+++ b/src/net_types.cpp
@@ -0,0 +1,65 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <net_types.h>
+
+#include <netaddress.h>
+#include <netbase.h>
+#include <univalue.h>
+
+CBanEntry::CBanEntry(const UniValue& json)
+ : nVersion(json["version"].get_int()), nCreateTime(json["ban_created"].get_int64()),
+ nBanUntil(json["banned_until"].get_int64())
+{
+}
+
+UniValue CBanEntry::ToJson() const
+{
+ UniValue json(UniValue::VOBJ);
+ json.pushKV("version", nVersion);
+ json.pushKV("ban_created", nCreateTime);
+ json.pushKV("banned_until", nBanUntil);
+ return json;
+}
+
+static const char* BANMAN_JSON_ADDR_KEY = "address";
+
+/**
+ * Convert a `banmap_t` object to a JSON array.
+ * @param[in] bans Bans list to convert.
+ * @return a JSON array, similar to the one returned by the `listbanned` RPC. Suitable for
+ * passing to `BanMapFromJson()`.
+ */
+UniValue BanMapToJson(const banmap_t& bans)
+{
+ UniValue bans_json(UniValue::VARR);
+ for (const auto& it : bans) {
+ const auto& address = it.first;
+ const auto& ban_entry = it.second;
+ UniValue j = ban_entry.ToJson();
+ j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
+ bans_json.push_back(j);
+ }
+ return bans_json;
+}
+
+/**
+ * Convert a JSON array to a `banmap_t` object.
+ * @param[in] bans_json JSON to convert, must be as returned by `BanMapToJson()`.
+ * @param[out] bans Bans list to create from the JSON.
+ * @throws std::runtime_error if the JSON does not have the expected fields or they contain
+ * unparsable values.
+ */
+void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
+{
+ for (const auto& ban_entry_json : bans_json.getValues()) {
+ CSubNet subnet;
+ const auto& subnet_str = ban_entry_json[BANMAN_JSON_ADDR_KEY].get_str();
+ if (!LookupSubNet(subnet_str, subnet)) {
+ throw std::runtime_error(
+ strprintf("Cannot parse banned address or subnet: %s", subnet_str));
+ }
+ bans.insert_or_assign(subnet, CBanEntry{ban_entry_json});
+ }
+}
diff --git a/src/net_types.h b/src/net_types.h
index d55a8cde6c..ffdc24c772 100644
--- a/src/net_types.h
+++ b/src/net_types.h
@@ -5,11 +5,56 @@
#ifndef BITCOIN_NET_TYPES_H
#define BITCOIN_NET_TYPES_H
+#include <cstdint>
#include <map>
-class CBanEntry;
class CSubNet;
+class UniValue;
+
+class CBanEntry
+{
+public:
+ static constexpr int CURRENT_VERSION{1};
+ int nVersion{CBanEntry::CURRENT_VERSION};
+ int64_t nCreateTime{0};
+ int64_t nBanUntil{0};
+
+ CBanEntry() {}
+
+ explicit CBanEntry(int64_t nCreateTimeIn)
+ : nCreateTime{nCreateTimeIn} {}
+
+ /**
+ * Create a ban entry from JSON.
+ * @param[in] json A JSON representation of a ban entry, as created by `ToJson()`.
+ * @throw std::runtime_error if the JSON does not have the expected fields.
+ */
+ explicit CBanEntry(const UniValue& json);
+
+ /**
+ * Generate a JSON representation of this ban entry.
+ * @return JSON suitable for passing to the `CBanEntry(const UniValue&)` constructor.
+ */
+ UniValue ToJson() const;
+};
using banmap_t = std::map<CSubNet, CBanEntry>;
+/**
+ * Convert a `banmap_t` object to a JSON array.
+ * @param[in] bans Bans list to convert.
+ * @return a JSON array, similar to the one returned by the `listbanned` RPC. Suitable for
+ * passing to `BanMapFromJson()`.
+ */
+UniValue BanMapToJson(const banmap_t& bans);
+
+/**
+ * Convert a JSON array to a `banmap_t` object.
+ * @param[in] bans_json JSON to convert, must be as returned by `BanMapToJson()`.
+ * @param[out] bans Bans list to create from the JSON.
+ * @throws std::runtime_error if the JSON does not have the expected fields or they contain
+ * unparsable values.
+ */
+void BanMapFromJson(const UniValue& bans_json, banmap_t& bans);
+
#endif // BITCOIN_NET_TYPES_H
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index e7b3377475..b2f4945e3b 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -1242,8 +1242,3 @@ bool operator<(const CSubNet& a, const CSubNet& b)
{
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
}
-
-bool SanityCheckASMap(const std::vector<bool>& asmap)
-{
- return SanityCheckASMap(asmap, 128); // For IP address lookups, the input is 128 bits
-}
diff --git a/src/netaddress.h b/src/netaddress.h
index eb35ed3fac..cfb2edcd34 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -567,6 +567,4 @@ public:
}
};
-bool SanityCheckASMap(const std::vector<bool>& asmap);
-
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 75202e7cf4..d14a20b870 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -180,6 +180,23 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
}
+std::vector<CKeyID> XOnlyPubKey::GetKeyIDs() const
+{
+ std::vector<CKeyID> out;
+ // For now, use the old full pubkey-based key derivation logic. As it is indexed by
+ // Hash160(full pubkey), we need to return both a version prefixed with 0x02, and one
+ // with 0x03.
+ unsigned char b[33] = {0x02};
+ std::copy(m_keydata.begin(), m_keydata.end(), b + 1);
+ CPubKey fullpubkey;
+ fullpubkey.Set(b, b + 33);
+ out.push_back(fullpubkey.GetID());
+ b[0] = 0x03;
+ fullpubkey.Set(b, b + 33);
+ out.push_back(fullpubkey.GetID());
+ return out;
+}
+
bool XOnlyPubKey::IsFullyValid() const
{
secp256k1_xonly_pubkey pubkey;
@@ -333,6 +350,7 @@ void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
memcpy(chaincode.begin(), code+9, 32);
pubkey.Set(code+41, code+BIP32_EXTKEY_SIZE);
+ if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || !pubkey.IsFullyValid()) pubkey = CPubKey();
}
bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
diff --git a/src/pubkey.h b/src/pubkey.h
index eec34a89c2..861a2cf500 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -267,6 +267,11 @@ public:
/** Construct a Taproot tweaked output point with this point as internal key. */
std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const;
+ /** Returns a list of CKeyIDs for the CPubKeys that could have been used to create this XOnlyPubKey.
+ * This is needed for key lookups since keys are indexed by CKeyID.
+ */
+ std::vector<CKeyID> GetKeyIDs() const;
+
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
const unsigned char* data() const { return m_keydata.begin(); }
static constexpr size_t size() { return decltype(m_keydata)::size(); }
diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h
index 57f559fc14..4b5b38e43f 100644
--- a/src/qt/bantablemodel.h
+++ b/src/qt/bantablemodel.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_QT_BANTABLEMODEL_H
#define BITCOIN_QT_BANTABLEMODEL_H
+#include <addrdb.h>
#include <net.h>
#include <memory>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 55eba60bcd..862bdd3bfe 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -676,8 +676,8 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
- WalletView* wallet_view = new WalletView(platformStyle, walletFrame);
- if (!walletFrame->addWallet(walletModel, wallet_view)) return;
+ WalletView* wallet_view = new WalletView(walletModel, platformStyle, walletFrame);
+ if (!walletFrame->addView(wallet_view)) return;
rpcConsole->addWallet(walletModel);
if (m_wallet_selector->count() == 0) {
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 022f367422..f4d561286e 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -112,7 +112,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
WalletContext& context = *node.walletClient().context();
AddWallet(context, wallet);
WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 1976bee74b..89f2258c0d 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -167,7 +167,7 @@ void TestGUI(interfaces::Node& node)
WalletContext& context = *node.walletClient().context();
AddWallet(context, wallet);
WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 30c29eb356..5eeb2d5308 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -11,6 +11,7 @@
#include <qt/psbtoperationsdialog.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
+#include <util/system.h>
#include <cassert>
@@ -64,14 +65,13 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
}
}
-bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
+bool WalletFrame::addView(WalletView* walletView)
{
- if (!clientModel || !walletModel) return false;
+ if (!clientModel) return false;
- if (mapWalletViews.count(walletModel) > 0) return false;
+ if (mapWalletViews.count(walletView->getWalletModel()) > 0) return false;
walletView->setClientModel(clientModel);
- walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
WalletView* current_wallet_view = currentWalletView();
@@ -82,7 +82,7 @@ bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
}
walletStack->addWidget(walletView);
- mapWalletViews[walletModel] = walletView;
+ mapWalletViews[walletView->getWalletModel()] = walletView;
return true;
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index cbf6af95ec..cfca5c4c5c 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -35,7 +35,7 @@ public:
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel* walletModel, WalletView* walletView);
+ bool addView(WalletView* walletView);
void setCurrentWallet(WalletModel* wallet_model);
void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 7e96e85c0c..309806a1c4 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -30,19 +30,24 @@
#include <QPushButton>
#include <QVBoxLayout>
-WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
- QStackedWidget(parent),
- clientModel(nullptr),
- walletModel(nullptr),
- platformStyle(_platformStyle)
+WalletView::WalletView(WalletModel* wallet_model, const PlatformStyle* _platformStyle, QWidget* parent)
+ : QStackedWidget(parent),
+ clientModel(nullptr),
+ walletModel(wallet_model),
+ platformStyle(_platformStyle)
{
+ assert(walletModel);
+
// Create tabs
overviewPage = new OverviewPage(platformStyle);
+ overviewPage->setWalletModel(walletModel);
transactionsPage = new QWidget(this);
QVBoxLayout *vbox = new QVBoxLayout();
QHBoxLayout *hbox_buttons = new QHBoxLayout();
transactionView = new TransactionView(platformStyle, this);
+ transactionView->setModel(walletModel);
+
vbox->addWidget(transactionView);
QPushButton *exportButton = new QPushButton(tr("&Export"), this);
exportButton->setToolTip(tr("Export the data in the current tab to a file"));
@@ -55,10 +60,16 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
transactionsPage->setLayout(vbox);
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
+ receiveCoinsPage->setModel(walletModel);
+
sendCoinsPage = new SendCoinsDialog(platformStyle);
+ sendCoinsPage->setModel(walletModel);
usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
+ usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
+
usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
+ usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
addWidget(overviewPage);
addWidget(transactionsPage);
@@ -84,6 +95,21 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(transactionView, &TransactionView::message, this, &WalletView::message);
connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy);
+
+ // Receive and pass through messages from wallet model
+ connect(walletModel, &WalletModel::message, this, &WalletView::message);
+
+ // Handle changes in encryption status
+ connect(walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
+
+ // Balloon pop-up for new transaction
+ connect(walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);
+
+ // Ask for passphrase if needed
+ connect(walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);
+
+ // Show progress dialog
+ connect(walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
}
WalletView::~WalletView()
@@ -96,45 +122,15 @@ void WalletView::setClientModel(ClientModel *_clientModel)
overviewPage->setClientModel(_clientModel);
sendCoinsPage->setClientModel(_clientModel);
- if (walletModel) walletModel->setClientModel(_clientModel);
-}
-
-void WalletView::setWalletModel(WalletModel *_walletModel)
-{
- this->walletModel = _walletModel;
-
- // Put transaction list in tabs
- transactionView->setModel(_walletModel);
- overviewPage->setWalletModel(_walletModel);
- receiveCoinsPage->setModel(_walletModel);
- sendCoinsPage->setModel(_walletModel);
- usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
- usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
-
- if (_walletModel)
- {
- // Receive and pass through messages from wallet model
- connect(_walletModel, &WalletModel::message, this, &WalletView::message);
-
- // Handle changes in encryption status
- connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
-
- // Balloon pop-up for new transaction
- connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);
-
- // Ask for passphrase if needed
- connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);
-
- // Show progress dialog
- connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
- }
+ walletModel->setClientModel(_clientModel);
}
void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
// Prevent balloon-spam when initial block download is in progress
- if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload())
+ if (!clientModel || clientModel->node().isInitialBlockDownload()) {
return;
+ }
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
if (!ttm || ttm->processingQueuedTransactions())
@@ -209,8 +205,6 @@ void WalletView::showOutOfSyncWarning(bool fShow)
void WalletView::encryptWallet()
{
- if(!walletModel)
- return;
AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
dlg.setModel(walletModel);
dlg.exec();
@@ -247,8 +241,6 @@ void WalletView::changePassphrase()
void WalletView::unlockWallet()
{
- if(!walletModel)
- return;
// Unlock wallet when requested by wallet model
if (walletModel->getEncryptionStatus() == WalletModel::Locked)
{
@@ -260,17 +252,11 @@ void WalletView::unlockWallet()
void WalletView::usedSendingAddresses()
{
- if(!walletModel)
- return;
-
GUIUtil::bringToFront(usedSendingAddressesPage);
}
void WalletView::usedReceivingAddresses()
{
- if(!walletModel)
- return;
-
GUIUtil::bringToFront(usedReceivingAddressesPage);
}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index bb6ad0f69e..eebc163624 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -35,19 +35,14 @@ class WalletView : public QStackedWidget
Q_OBJECT
public:
- explicit WalletView(const PlatformStyle *platformStyle, QWidget *parent);
+ explicit WalletView(WalletModel* wallet_model, const PlatformStyle* platformStyle, QWidget* parent);
~WalletView();
/** Set the client model.
The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic.
*/
void setClientModel(ClientModel *clientModel);
- WalletModel *getWalletModel() { return walletModel; }
- /** Set the wallet model.
- The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending
- functionality.
- */
- void setWalletModel(WalletModel *walletModel);
+ WalletModel* getWalletModel() const noexcept { return walletModel; }
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
@@ -55,7 +50,12 @@ public:
private:
ClientModel *clientModel;
- WalletModel *walletModel;
+
+ //!
+ //! The wallet model represents a bitcoin wallet, and offers access to
+ //! the list of transactions, address book and sending functionality.
+ //!
+ WalletModel* const walletModel;
OverviewPage *overviewPage;
QWidget *transactionsPage;
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 692096367c..27cbb3a702 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1089,7 +1089,8 @@ static RPCHelpMan estimatesmartfee()
"have been observed to make an estimate for any number of blocks."},
}},
RPCExamples{
- HelpExampleCli("estimatesmartfee", "6")
+ HelpExampleCli("estimatesmartfee", "6") +
+ HelpExampleRpc("estimatesmartfee", "6")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 1a94abf6d3..e5804211f3 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -109,7 +109,7 @@ static RPCHelpMan createmultisig()
"\nCreate a multisig address from 2 public keys\n"
+ HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
"\nAs a JSON-RPC call\n"
- + HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"")
+ + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -158,6 +158,8 @@ static RPCHelpMan createmultisig()
static RPCHelpMan getdescriptorinfo()
{
+ const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)";
+
return RPCHelpMan{"getdescriptorinfo",
{"\nAnalyses a descriptor.\n"},
{
@@ -175,7 +177,8 @@ static RPCHelpMan getdescriptorinfo()
},
RPCExamples{
"Analyse a descriptor\n" +
- HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
+ HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") +
+ HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -201,6 +204,8 @@ static RPCHelpMan getdescriptorinfo()
static RPCHelpMan deriveaddresses()
{
+ const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
+
return RPCHelpMan{"deriveaddresses",
{"\nDerives one or more addresses corresponding to an output descriptor.\n"
"Examples of output descriptors are:\n"
@@ -223,7 +228,8 @@ static RPCHelpMan deriveaddresses()
},
RPCExamples{
"First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
+ HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") +
+ HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 682b55742a..621a1b9fd6 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1242,14 +1242,8 @@ std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseS
CPubKey pubkey(full_key);
std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true);
KeyOriginInfo info;
- if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
+ if (provider.GetKeyOriginByXOnly(xkey, info)) {
return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
- } else {
- full_key[0] = 0x03;
- pubkey = CPubKey(full_key);
- if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
- return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
- }
}
return key_provider;
}
diff --git a/src/script/script.h b/src/script/script.h
index 974cde4984..8cd1cc3855 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_SCRIPT_H
#define BITCOIN_SCRIPT_SCRIPT_H
+#include <attributes.h>
#include <crypto/common.h>
#include <prevector.h>
#include <serialize.h>
@@ -438,9 +439,9 @@ public:
/** Delete non-existent operator to defend against future introduction */
CScript& operator<<(const CScript& b) = delete;
- CScript& operator<<(int64_t b) { return push_int64(b); }
+ CScript& operator<<(int64_t b) LIFETIMEBOUND { return push_int64(b); }
- CScript& operator<<(opcodetype opcode)
+ CScript& operator<<(opcodetype opcode) LIFETIMEBOUND
{
if (opcode < 0 || opcode > 0xff)
throw std::runtime_error("CScript::operator<<(): invalid opcode");
@@ -448,13 +449,13 @@ public:
return *this;
}
- CScript& operator<<(const CScriptNum& b)
+ CScript& operator<<(const CScriptNum& b) LIFETIMEBOUND
{
*this << b.getvch();
return *this;
}
- CScript& operator<<(const std::vector<unsigned char>& b)
+ CScript& operator<<(const std::vector<unsigned char>& b) LIFETIMEBOUND
{
if (b.size() < OP_PUSHDATA1)
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 4714d0ef11..b912b00365 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -60,22 +60,7 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
CKey key;
- {
- // For now, use the old full pubkey-based key derivation logic. As it is indexed by
- // Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
- // with 0x03.
- unsigned char b[33] = {0x02};
- std::copy(pubkey.begin(), pubkey.end(), b + 1);
- CPubKey fullpubkey;
- fullpubkey.Set(b, b + 33);
- CKeyID keyid = fullpubkey.GetID();
- if (!provider.GetKey(keyid, key)) {
- b[0] = 0x03;
- fullpubkey.Set(b, b + 33);
- CKeyID keyid = fullpubkey.GetID();
- if (!provider.GetKey(keyid, key)) return false;
- }
- }
+ if (!provider.GetKeyByXOnly(pubkey, key)) return false;
// BIP341/BIP342 signing needs lots of precomputed transaction data. While some
// (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 939ae10622..fbce61c6a9 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -26,6 +26,30 @@ public:
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
+
+ bool GetKeyByXOnly(const XOnlyPubKey& pubkey, CKey& key) const
+ {
+ for (const auto& id : pubkey.GetKeyIDs()) {
+ if (GetKey(id, key)) return true;
+ }
+ return false;
+ }
+
+ bool GetPubKeyByXOnly(const XOnlyPubKey& pubkey, CPubKey& out) const
+ {
+ for (const auto& id : pubkey.GetKeyIDs()) {
+ if (GetPubKey(id, out)) return true;
+ }
+ return false;
+ }
+
+ bool GetKeyOriginByXOnly(const XOnlyPubKey& pubkey, KeyOriginInfo& info) const
+ {
+ for (const auto& id : pubkey.GetKeyIDs()) {
+ if (GetKeyOrigin(id, info)) return true;
+ }
+ return false;
+ }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
diff --git a/src/signet.cpp b/src/signet.cpp
index 1ba8502287..aafd1999ee 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -141,7 +141,7 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
PrecomputedTransactionData txdata;
txdata.Init(signet_txs->m_to_sign, {signet_txs->m_to_spend.vout[0]});
- TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
+ TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /* nInIn= */ 0, /* amountIn= */ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
if (!VerifyScript(scriptSig, signet_txs->m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
diff --git a/src/sync.cpp b/src/sync.cpp
index a2b62c2286..98e6d3d65d 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -23,17 +23,6 @@
#include <utility>
#include <vector>
-#ifdef DEBUG_LOCKCONTENTION
-#if !defined(HAVE_THREAD_LOCAL)
-static_assert(false, "thread_local is not supported");
-#endif
-void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
-{
- LogPrintf("LOCKCONTENTION: %s\n", pszName);
- LogPrintf("Locker: %s:%d\n", pszFile, nLine);
-}
-#endif /* DEBUG_LOCKCONTENTION */
-
#ifdef DEBUG_LOCKORDER
//
// Early deadlock detection.
diff --git a/src/sync.h b/src/sync.h
index 146c228592..6ba63d5e4d 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_SYNC_H
#define BITCOIN_SYNC_H
+#include <logging.h>
+#include <logging/timer.h>
#include <threadsafety.h>
#include <util/macros.h>
@@ -126,10 +128,6 @@ using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
/** Wrapped mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<std::mutex> Mutex;
-#ifdef DEBUG_LOCKCONTENTION
-void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
-#endif
-
/** Wrapper around std::unique_lock style lock for Mutex. */
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
class SCOPED_LOCKABLE UniqueLock : public Base
@@ -138,22 +136,18 @@ private:
void Enter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex());
-#ifdef DEBUG_LOCKCONTENTION
- if (!Base::try_lock()) {
- PrintLockContention(pszName, pszFile, nLine);
-#endif
- Base::lock();
-#ifdef DEBUG_LOCKCONTENTION
- }
-#endif
+ if (Base::try_lock()) return;
+ LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
+ Base::lock();
}
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
Base::try_lock();
- if (!Base::owns_lock())
+ if (!Base::owns_lock()) {
LeaveCritical();
+ }
return Base::owns_lock();
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index cd5dc2370f..835b18d42e 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -5,13 +5,14 @@
#include <addrdb.h>
#include <addrman.h>
#include <chainparams.h>
+#include <clientversion.h>
+#include <hash.h>
+#include <netbase.h>
+#include <random.h>
#include <test/data/asmap.raw.h>
#include <test/util/setup_common.h>
#include <util/asmap.h>
#include <util/string.h>
-#include <hash.h>
-#include <netbase.h>
-#include <random.h>
#include <boost/test/unit_test.hpp>
@@ -26,7 +27,7 @@ public:
virtual void Serialize(CDataStream& s) const = 0;
CAddrManSerializationMock()
- : CAddrMan(/* deterministic */ true, /* consistency_check_ratio */ 100)
+ : CAddrMan(/* asmap */ std::vector<bool>(), /* deterministic */ true, /* consistency_check_ratio */ 100)
{}
};
@@ -82,10 +83,9 @@ private:
public:
explicit CAddrManTest(bool makeDeterministic = true,
std::vector<bool> asmap = std::vector<bool>())
- : CAddrMan(makeDeterministic, /* consistency_check_ratio */ 100)
+ : CAddrMan(asmap, makeDeterministic, /* consistency_check_ratio */ 100)
{
deterministic = makeDeterministic;
- m_asmap = asmap;
}
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
@@ -1003,7 +1003,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
}
-BOOST_AUTO_TEST_CASE(caddrdb_read)
+BOOST_AUTO_TEST_CASE(load_addrman)
{
CAddrManUncorrupted addrmanUncorrupted;
@@ -1024,7 +1024,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
bool exceptionThrown = false;
- CAddrMan addrman1(/* deterministic */ false, /* consistency_check_ratio */ 100);
+ CAddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman1.size() == 0);
try {
@@ -1038,24 +1038,24 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_CHECK(addrman1.size() == 3);
BOOST_CHECK(exceptionThrown == false);
- // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
+ // Test that ReadFromStream creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);
- CAddrMan addrman2(/* deterministic */ false, /* consistency_check_ratio */ 100);
+ CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(CAddrDB::Read(addrman2, ssPeers2));
+ BOOST_CHECK(ReadFromStream(addrman2, ssPeers2));
BOOST_CHECK(addrman2.size() == 3);
}
-BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
+BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
{
CAddrManCorrupted addrmanCorrupted;
// Test that the de-serialization of corrupted addrman throws an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
bool exceptionThrown = false;
- CAddrMan addrman1(/* deterministic */ false, /* consistency_check_ratio */ 100);
+ CAddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
@@ -1064,16 +1064,16 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
} catch (const std::exception&) {
exceptionThrown = true;
}
- // Even through de-serialization failed addrman is not left in a clean state.
+ // Even though de-serialization failed addrman is not left in a clean state.
BOOST_CHECK(addrman1.size() == 1);
BOOST_CHECK(exceptionThrown);
- // Test that CAddrDB::Read fails if peers.dat is corrupt
+ // Test that ReadFromStream fails if peers.dat is corrupt
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);
- CAddrMan addrman2(/* deterministic */ false, /* consistency_check_ratio */ 100);
+ CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(!CAddrDB::Read(addrman2, ssPeers2));
+ BOOST_CHECK(!ReadFromStream(addrman2, ssPeers2));
}
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index fb16c92647..a89868e1ef 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -14,6 +14,8 @@
#include <string>
#include <vector>
+namespace {
+
struct TestDerivation {
std::string pub;
std::string prv;
@@ -99,7 +101,26 @@ TestVector test4 =
"xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1",
0);
-static void RunTest(const TestVector &test) {
+const std::vector<std::string> TEST5 = {
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J",
+ "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv",
+ "xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ",
+ "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN",
+ "xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8",
+ "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4",
+ "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY",
+ "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL"
+};
+
+void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
CExtPubKey pubkey;
@@ -133,6 +154,8 @@ static void RunTest(const TestVector &test) {
}
}
+} // namespace
+
BOOST_FIXTURE_TEST_SUITE(bip32_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(bip32_test1) {
@@ -151,4 +174,13 @@ BOOST_AUTO_TEST_CASE(bip32_test4) {
RunTest(test4);
}
+BOOST_AUTO_TEST_CASE(bip32_test5) {
+ for (const auto& str : TEST5) {
+ auto dec_extkey = DecodeExtKey(str);
+ auto dec_extpubkey = DecodeExtPubKey(str);
+ BOOST_CHECK_MESSAGE(!dec_extkey.key.IsValid(), "Decoding '" + str + "' as xprv should fail");
+ BOOST_CHECK_MESSAGE(!dec_extpubkey.pubkey.IsValid(), "Decoding '" + str + "' as xpub should fail");
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/addrdb.cpp b/src/test/fuzz/addrdb.cpp
deleted file mode 100644
index 44df3576c4..0000000000
--- a/src/test/fuzz/addrdb.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2020 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <addrdb.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-
-#include <cassert>
-#include <cstdint>
-#include <optional>
-#include <string>
-#include <vector>
-
-FUZZ_TARGET(addrdb)
-{
- FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
-
- // The point of this code is to exercise all CBanEntry constructors.
- const CBanEntry ban_entry{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
- (void)ban_entry; // currently unused
-}
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 95aa53bff4..fdbfb3b93b 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -28,17 +28,11 @@ class CAddrManDeterministic : public CAddrMan
public:
FuzzedDataProvider& m_fuzzed_data_provider;
- explicit CAddrManDeterministic(FuzzedDataProvider& fuzzed_data_provider)
- : CAddrMan(/* deterministic */ true, /* consistency_check_ratio */ 0)
+ explicit CAddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
+ : CAddrMan(std::move(asmap), /* deterministic */ true, /* consistency_check_ratio */ 0)
, m_fuzzed_data_provider(fuzzed_data_provider)
{
WITH_LOCK(cs, insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
- if (fuzzed_data_provider.ConsumeBool()) {
- m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (!SanityCheckASMap(m_asmap)) {
- m_asmap.clear();
- }
- }
}
/**
@@ -224,11 +218,19 @@ public:
}
};
+[[nodiscard]] inline std::vector<bool> ConsumeAsmap(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+ if (!SanityCheckASMap(asmap, 128)) asmap.clear();
+ return asmap;
+}
+
FUZZ_TARGET_INIT(addrman, initialize_addrman)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
- auto addr_man_ptr = std::make_unique<CAddrManDeterministic>(fuzzed_data_provider);
+ std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
+ auto addr_man_ptr = std::make_unique<CAddrManDeterministic>(asmap, fuzzed_data_provider);
if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
@@ -237,7 +239,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
try {
ds >> *addr_man_ptr;
} catch (const std::ios_base::failure&) {
- addr_man_ptr = std::make_unique<CAddrManDeterministic>(fuzzed_data_provider);
+ addr_man_ptr = std::make_unique<CAddrManDeterministic>(asmap, fuzzed_data_provider);
}
}
CAddrManDeterministic& addr_man = *addr_man_ptr;
@@ -306,9 +308,9 @@ FUZZ_TARGET_INIT(addrman_serdeser, initialize_addrman)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
- CAddrManDeterministic addr_man1{fuzzed_data_provider};
- CAddrManDeterministic addr_man2{fuzzed_data_provider};
- addr_man2.m_asmap = addr_man1.m_asmap;
+ std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
+ CAddrManDeterministic addr_man1{asmap, fuzzed_data_provider};
+ CAddrManDeterministic addr_man2{asmap, fuzzed_data_provider};
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
index 4c5bc0cbf2..d402f8632c 100644
--- a/src/test/fuzz/asmap.cpp
+++ b/src/test/fuzz/asmap.cpp
@@ -4,6 +4,7 @@
#include <netaddress.h>
#include <test/fuzz/fuzz.h>
+#include <util/asmap.h>
#include <cstdint>
#include <vector>
@@ -42,7 +43,7 @@ FUZZ_TARGET(asmap)
asmap.push_back((buffer[1 + i] >> j) & 1);
}
}
- if (!SanityCheckASMap(asmap)) return;
+ if (!SanityCheckASMap(asmap, 128)) return;
const uint8_t* addr_data = buffer.data() + 1 + asmap_size;
CNetAddr net_addr;
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 0e323ddc20..01741103e4 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -25,7 +25,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman, fuzzed_data_provider.ConsumeBool()};
CNetAddr random_netaddr;
CNode random_node = ConsumeNode(fuzzed_data_provider);
@@ -104,12 +104,6 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
connman.RemoveAddedNode(random_string);
},
[&] {
- const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (SanityCheckASMap(asmap)) {
- connman.SetAsmap(asmap);
- }
- },
- [&] {
connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
},
[&] {
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index 53400082ab..08e9c91ba3 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.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 <addrdb.h>
#include <addrman.h>
#include <net.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -21,6 +22,6 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
- CAddrMan addr_man(/* deterministic */ false, /* consistency_check_ratio */ 0);
- CAddrDB::Read(addr_man, data_stream);
+ CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ ReadFromStream(addr_man, data_stream);
}
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index cfbbe77311..83ae1680e3 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -188,7 +188,7 @@ FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
BlockMerkleRoot(block, &mutated);
})
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
- CAddrMan am(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ CAddrMan am(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
DeserializeFromFuzzingInput(buffer, am);
})
FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 5d26529837..5a732aeeff 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -23,6 +23,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <uint256.h>
+#include <univalue.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 9a579c053f..bd1bb79d0e 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -14,6 +14,7 @@
#include <test/fuzz/util.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
+#include <util/asmap.h>
#include <cstdint>
#include <optional>
@@ -38,12 +39,8 @@ FUZZ_TARGET_INIT(net, initialize_net)
node.CloseSocketDisconnect();
},
[&] {
- const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (!SanityCheckASMap(asmap)) {
- return;
- }
CNodeStats stats;
- node.copyStats(stats, asmap);
+ node.CopyStats(stats);
},
[&] {
const CNode* add_ref_node = node.AddRef();
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index 9186821836..73a7d24971 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -6,6 +6,7 @@
#include <chainparams.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/logging_tests.cpp b/src/test/logging_tests.cpp
index e99c6e0fc8..84ddbc50c6 100644
--- a/src/test/logging_tests.cpp
+++ b/src/test/logging_tests.cpp
@@ -15,9 +15,9 @@ BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(logging_timer)
{
SetMockTime(1);
- auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
+ auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
SetMockTime(2);
- BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
+ BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000μs)");
SetMockTime(1);
auto ms_timer = BCLog::Timer<std::chrono::milliseconds>("tests", "end_msg");
@@ -25,9 +25,9 @@ BOOST_AUTO_TEST_CASE(logging_timer)
BOOST_CHECK_EQUAL(ms_timer.LogMsg("test ms"), "tests: test ms (1000.00ms)");
SetMockTime(1);
- auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
+ auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
SetMockTime(2);
- BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000.00μs)");
+ BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 97fd0600fa..24029ea02e 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -765,95 +765,89 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
key.MakeNewKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
- std::string reason;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ constexpr auto CheckIsStandard = [](const auto& t) {
+ std::string reason;
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK(reason.empty());
+ };
+ constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) {
+ std::string reason;
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason_in, reason);
+ };
+
+ CheckIsStandard(t);
// Check dust with default relay fee:
- CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000;
+ CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK() / 1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "dust");
+ CheckIsNotStandard(t, "dust");
// not dust:
t.vout[0].nValue = nDustThreshold;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// Disallowed nVersion
t.nVersion = -1;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "version");
+ CheckIsNotStandard(t, "version");
t.nVersion = 0;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "version");
+ CheckIsNotStandard(t, "version");
t.nVersion = 3;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "version");
+ CheckIsNotStandard(t, "version");
// Allowed nVersion
t.nVersion = 1;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
t.nVersion = 2;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 3702 / 1000
dustRelayFee = CFeeRate(3702);
// dust:
t.vout[0].nValue = 673 - 1;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "dust");
+ CheckIsNotStandard(t, "dust");
// not dust:
t.vout[0].nValue = 673;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
t.vout[0].scriptPubKey = CScript() << OP_1;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "scriptpubkey");
+ CheckIsNotStandard(t, "scriptpubkey");
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "scriptpubkey");
+ CheckIsNotStandard(t, "scriptpubkey");
// Data payload can be encoded in any way...
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01");
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()!
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0 << ParseHex("01") << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// ...so long as it only contains PUSHDATA's
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "scriptpubkey");
+ CheckIsNotStandard(t, "scriptpubkey");
// TxoutType::NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// Only one TxoutType::NULL_DATA permitted in all cases
t.vout.resize(2);
@@ -861,21 +855,15 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 0;
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].nValue = 0;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "multi-op-return");
+ CheckIsNotStandard(t, "multi-op-return");
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "multi-op-return");
+ CheckIsNotStandard(t, "multi-op-return");
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "multi-op-return");
+ CheckIsNotStandard(t, "multi-op-return");
// Check large scriptSig (non-standard if size is >1650 bytes)
t.vout.resize(1);
@@ -883,12 +871,10 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
// OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1647, 0); // 1650
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1648, 0); // 1651
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "scriptsig-size");
+ CheckIsNotStandard(t, "scriptsig-size");
// Check scriptSig format (non-standard if there are any other ops than just PUSHs)
t.vin[0].scriptSig = CScript()
@@ -897,7 +883,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
<< std::vector<unsigned char>(235, 0) // OP_PUSHDATA1 x [...x bytes...]
<< std::vector<unsigned char>(1234, 0) // OP_PUSHDATA2 x [...x bytes...]
<< OP_9;
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
const std::vector<unsigned char> non_push_ops = { // arbitrary set of non-push operations
OP_NOP, OP_VERIFY, OP_IF, OP_ROT, OP_3DUP, OP_SIZE, OP_EQUAL, OP_ADD, OP_SUB,
@@ -917,11 +903,10 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
// replace current push-op with each non-push-op
for (auto op : non_push_ops) {
t.vin[0].scriptSig[index] = op;
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "scriptsig-not-pushonly");
+ CheckIsNotStandard(t, "scriptsig-not-pushonly");
}
t.vin[0].scriptSig[index] = orig_op; // restore op
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
}
// Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT)
@@ -934,27 +919,47 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
// ===============================
// total: 400000 vbytes
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400000);
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
// increase output size by one byte, so we end up with 400004 vbytes
t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(20, 0); // output size: 31 bytes
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400004);
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "tx-size");
+ CheckIsNotStandard(t, "tx-size");
// Check bare multisig (standard if policy flag fIsBareMultisigStd is set)
fIsBareMultisigStd = true;
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1
t.vin.resize(1);
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
- BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ CheckIsStandard(t);
fIsBareMultisigStd = false;
- reason.clear();
- BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
- BOOST_CHECK_EQUAL(reason, "bare-multisig");
+ CheckIsNotStandard(t, "bare-multisig");
fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
+
+ // Check P2WPKH outputs dust threshold
+ t.vout[0].scriptPubKey = CScript() << OP_0 << ParseHex("ffffffffffffffffffffffffffffffffffffffff");
+ t.vout[0].nValue = 294;
+ CheckIsStandard(t);
+ t.vout[0].nValue = 293;
+ CheckIsNotStandard(t, "dust");
+
+ // Check P2WSH outputs dust threshold
+ t.vout[0].scriptPubKey = CScript() << OP_0 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+ t.vout[0].nValue = 330;
+ CheckIsStandard(t);
+ t.vout[0].nValue = 329;
+ CheckIsNotStandard(t, "dust");
+
+ // Check future Witness Program versions dust threshold
+ for (int op = OP_2; op <= OP_16; op += 1) {
+ t.vout[0].scriptPubKey = CScript() << (opcodetype)op << ParseHex("ffff");
+ t.vout[0].nValue = 240;
+ CheckIsStandard(t);
+
+ t.vout[0].nValue = 239;
+ CheckIsNotStandard(t, "dust");
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index c9bb863a7b..ba6b3e32ea 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -193,7 +193,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
- m_node.addrman = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ 0);
+ m_node.addrman = std::make_unique<CAddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 4f9d10cf1d..a62abf9b9c 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -173,6 +173,22 @@ BOOST_AUTO_TEST_CASE(util_Join)
BOOST_CHECK_EQUAL(Join<std::string>({"foo", "bar"}, ", ", op_upper), "FOO, BAR");
}
+BOOST_AUTO_TEST_CASE(util_TrimString)
+{
+ BOOST_CHECK_EQUAL(TrimString(" foo bar "), "foo bar");
+ BOOST_CHECK_EQUAL(TrimString("\t \n \n \f\n\r\t\v\tfoo \n \f\n\r\t\v\tbar\t \n \f\n\r\t\v\t\n "), "foo \n \f\n\r\t\v\tbar");
+ BOOST_CHECK_EQUAL(TrimString("\t \n foo \n\tbar\t \n "), "foo \n\tbar");
+ BOOST_CHECK_EQUAL(TrimString("\t \n foo \n\tbar\t \n ", "fobar"), "\t \n foo \n\tbar\t \n ");
+ BOOST_CHECK_EQUAL(TrimString("foo bar"), "foo bar");
+ BOOST_CHECK_EQUAL(TrimString("foo bar", "fobar"), " ");
+ BOOST_CHECK_EQUAL(TrimString(std::string("\0 foo \0 ", 8)), std::string("\0 foo \0", 7));
+ BOOST_CHECK_EQUAL(TrimString(std::string(" foo ", 5)), std::string("foo", 3));
+ BOOST_CHECK_EQUAL(TrimString(std::string("\t\t\0\0\n\n", 6)), std::string("\0\0", 2));
+ BOOST_CHECK_EQUAL(TrimString(std::string("\x05\x04\x03\x02\x01\x00", 6)), std::string("\x05\x04\x03\x02\x01\x00", 6));
+ BOOST_CHECK_EQUAL(TrimString(std::string("\x05\x04\x03\x02\x01\x00", 6), std::string("\x05\x04\x03\x02\x01", 5)), std::string("\0", 1));
+ BOOST_CHECK_EQUAL(TrimString(std::string("\x05\x04\x03\x02\x01\x00", 6), std::string("\x05\x04\x03\x02\x01\x00", 6)), "");
+}
+
BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime)
{
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index bacc3690a2..5695c62012 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -2,10 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/asmap.h>
+
+#include <clientversion.h>
+#include <crypto/common.h>
+#include <logging.h>
+#include <streams.h>
+
+#include <cassert>
#include <map>
#include <vector>
-#include <assert.h>
-#include <crypto/common.h>
namespace {
@@ -183,3 +189,31 @@ bool SanityCheckASMap(const std::vector<bool>& asmap, int bits)
}
return false; // Reached EOF without RETURN instruction
}
+
+std::vector<bool> DecodeAsmap(fs::path path)
+{
+ std::vector<bool> bits;
+ FILE *filestr = fsbridge::fopen(path, "rb");
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ LogPrintf("Failed to open asmap file from disk\n");
+ return bits;
+ }
+ fseek(filestr, 0, SEEK_END);
+ int length = ftell(filestr);
+ LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
+ fseek(filestr, 0, SEEK_SET);
+ uint8_t cur_byte;
+ for (int i = 0; i < length; ++i) {
+ file >> cur_byte;
+ for (int bit = 0; bit < 8; ++bit) {
+ bits.push_back((cur_byte >> bit) & 1);
+ }
+ }
+ if (!SanityCheckASMap(bits, 128)) {
+ LogPrintf("Sanity check of asmap file %s failed\n", path);
+ return {};
+ }
+ return bits;
+}
+
diff --git a/src/util/asmap.h b/src/util/asmap.h
index d0588bc8c3..810d70b9a1 100644
--- a/src/util/asmap.h
+++ b/src/util/asmap.h
@@ -5,11 +5,16 @@
#ifndef BITCOIN_UTIL_ASMAP_H
#define BITCOIN_UTIL_ASMAP_H
-#include <stdint.h>
+#include <fs.h>
+
+#include <cstdint>
#include <vector>
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip);
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits);
+/** Read asmap from provided binary file */
+std::vector<bool> DecodeAsmap(fs::path path);
+
#endif // BITCOIN_UTIL_ASMAP_H
diff --git a/src/util/getuniquepath.cpp b/src/util/getuniquepath.cpp
index 9839d2f624..6776e7785b 100644
--- a/src/util/getuniquepath.cpp
+++ b/src/util/getuniquepath.cpp
@@ -1,3 +1,7 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
#include <random.h>
#include <fs.h>
#include <util/strencodings.h>
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
index b92b1d30c3..846b34089d 100644
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -60,9 +60,15 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
values.clear();
errors.clear();
+ // Ok for file to not exist
+ if (!fs::exists(path)) return true;
+
fsbridge::ifstream file;
file.open(path);
- if (!file.is_open()) return true; // Ok for file not to exist.
+ if (!file.is_open()) {
+ errors.emplace_back(strprintf("%s. Please check permissions.", path.string()));
+ return false;
+ }
SettingsValue in;
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 4e16a83c87..08f62f1da7 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1306,7 +1306,7 @@ void SetupEnvironment()
#endif
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
// may be invalid, in which case the "C.UTF-8" locale is used as fallback.
-#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
try {
std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
diff --git a/src/util/types.h b/src/util/types.h
new file mode 100644
index 0000000000..0047b00026
--- /dev/null
+++ b/src/util/types.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_TYPES_H
+#define BITCOIN_UTIL_TYPES_H
+
+template <class>
+inline constexpr bool ALWAYS_FALSE{false};
+
+#endif // BITCOIN_UTIL_TYPES_H
diff --git a/src/validation.cpp b/src/validation.cpp
index e1d218e358..8696f1af85 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -415,7 +415,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
}
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
- return CheckInputScripts(tx, state, view, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata);
+ return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
}
namespace {
diff --git a/src/validation.h b/src/validation.h
index ef48156309..d4fcac1d48 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -929,7 +929,7 @@ public:
CChainState& InitializeChainstate(
CTxMemPool* mempool,
const std::optional<uint256>& snapshot_blockhash = std::nullopt)
- EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
std::vector<CChainState*> GetAll();
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 30fef50c3b..f2de68295e 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -12,6 +12,8 @@
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
+#include <wallet/receive.h>
+#include <wallet/spend.h>
#include <wallet/wallet.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
@@ -30,7 +32,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
}
}
- if (wtx.GetDepthInMainChain() != 0) {
+ if (wallet.GetTxDepthInMainChain(wtx) != 0) {
errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
return feebumper::Result::WALLET_ERROR;
}
@@ -48,7 +50,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
// check that original tx consists entirely of our inputs
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- if (!wallet.IsAllFromMe(*wtx.tx, filter)) {
+ if (!AllInputsMine(wallet, *wtx.tx, filter)) {
errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
return feebumper::Result::WALLET_ERROR;
}
@@ -81,7 +83,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
// Given old total fee and transaction size, calculate the old feeRate
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- CAmount old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut();
+ CAmount old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
CFeeRate nOldFeeRate(old_fee, txSize);
// Min total fee is old fee + relay fee
@@ -174,7 +176,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
// Fill in recipients(and preserve a single change key if there is one)
std::vector<CRecipient> recipients;
for (const auto& output : wtx.tx->vout) {
- if (!wallet.IsChange(output)) {
+ if (!OutputIsChange(wallet, output)) {
CRecipient recipient = {output.scriptPubKey, output.nValue, false};
recipients.push_back(recipient);
} else {
@@ -185,7 +187,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut();
+ old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
@@ -220,7 +222,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
int change_pos_in_out = -1; // No requested location for change
bilingual_str fail_reason;
FeeCalculation fee_calc_out;
- if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
+ if (!CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR;
}
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 0d4b98ecaf..9a8c1e3c02 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -23,7 +23,9 @@
#include <wallet/fees.h>
#include <wallet/ismine.h>
#include <wallet/load.h>
+#include <wallet/receive.h>
#include <wallet/rpcwallet.h>
+#include <wallet/spend.h>
#include <wallet/wallet.h>
#include <memory>
@@ -55,7 +57,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size());
for (const auto& txin : wtx.tx->vin) {
- result.txin_is_mine.emplace_back(wallet.IsMine(txin));
+ result.txin_is_mine.emplace_back(InputIsMine(wallet, txin));
}
result.txout_is_mine.reserve(wtx.tx->vout.size());
result.txout_address.reserve(wtx.tx->vout.size());
@@ -67,9 +69,9 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
wallet.IsMine(result.txout_address.back()) :
ISMINE_NO);
}
- result.credit = wtx.GetCredit(ISMINE_ALL);
- result.debit = wtx.GetDebit(ISMINE_ALL);
- result.change = wtx.GetChange();
+ result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL);
+ result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL);
+ result.change = CachedTxGetChange(wallet, wtx);
result.time = wtx.GetTxTime();
result.value_map = wtx.mapValue;
result.is_coinbase = wtx.IsCoinBase();
@@ -81,15 +83,15 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
{
WalletTxStatus result;
result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max();
- result.blocks_to_maturity = wtx.GetBlocksToMaturity();
- result.depth_in_main_chain = wtx.GetDepthInMainChain();
+ result.blocks_to_maturity = wallet.GetTxBlocksToMaturity(wtx);
+ result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = wallet.chain().checkFinalTx(*wtx.tx);
- result.is_trusted = wtx.IsTrusted();
+ result.is_trusted = CachedTxIsTrusted(wallet, wtx);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
- result.is_in_main_chain = wtx.IsInMainChain();
+ result.is_in_main_chain = wallet.IsTxInMainChain(wtx);
return result;
}
@@ -242,7 +244,7 @@ public:
LOCK(m_wallet->cs_wallet);
CTransactionRef tx;
FeeCalculation fee_calc_out;
- if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos,
+ if (!CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
fail_reason, coin_control, fee_calc_out, sign)) {
return {};
}
@@ -358,7 +360,7 @@ public:
}
WalletBalances getBalances() override
{
- const auto bal = m_wallet->GetBalance();
+ const auto bal = GetBalance(*m_wallet);
WalletBalances result;
result.balance = bal.m_mine_trusted;
result.unconfirmed_balance = bal.m_mine_untrusted_pending;
@@ -381,15 +383,15 @@ public:
balances = getBalances();
return true;
}
- CAmount getBalance() override { return m_wallet->GetBalance().m_mine_trusted; }
+ CAmount getBalance() override { return GetBalance(*m_wallet).m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
- return m_wallet->GetAvailableBalance(&coin_control);
+ return GetAvailableBalance(*m_wallet, &coin_control);
}
isminetype txinIsMine(const CTxIn& txin) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->IsMine(txin);
+ return InputIsMine(*m_wallet, txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
@@ -404,13 +406,13 @@ public:
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->GetCredit(txout, filter);
+ return OutputGetCredit(*m_wallet, txout, filter);
}
CoinsList listCoins() override
{
LOCK(m_wallet->cs_wallet);
CoinsList result;
- for (const auto& entry : m_wallet->ListCoins()) {
+ for (const auto& entry : ListCoins(*m_wallet)) {
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
@@ -428,7 +430,7 @@ public:
result.emplace_back();
auto it = m_wallet->mapWallet.find(output.hash);
if (it != m_wallet->mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain();
+ int depth = m_wallet->GetTxDepthInMainChain(it->second);
if (depth >= 0) {
result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth);
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 85cdbb67c9..1b841026b8 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -13,6 +13,7 @@
#include <util/system.h>
#include <util/translation.h>
#include <wallet/context.h>
+#include <wallet/spend.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -164,7 +165,7 @@ void UnloadWallets(WalletContext& context)
auto wallet = wallets.back();
wallets.pop_back();
std::vector<bilingual_str> warnings;
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt, warnings);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);
UnloadWallet(std::move(wallet));
}
}
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index de81dbf324..98706dcdf8 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -7,27 +7,27 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
-isminetype CWallet::IsMine(const CTxIn &txin) const
+isminetype InputIsMine(const CWallet& wallet, const CTxIn &txin)
{
- AssertLockHeld(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
+ AssertLockHeld(wallet.cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator mi = wallet.mapWallet.find(txin.prevout.hash);
+ if (mi != wallet.mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.tx->vout.size())
- return IsMine(prev.tx->vout[txin.prevout.n]);
+ return wallet.IsMine(prev.tx->vout[txin.prevout.n]);
}
return ISMINE_NO;
}
-bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
+bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
{
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
for (const CTxIn& txin : tx.vin)
{
- auto mi = mapWallet.find(txin.prevout.hash);
- if (mi == mapWallet.end())
+ auto mi = wallet.mapWallet.find(txin.prevout.hash);
+ if (mi == wallet.mapWallet.end())
return false; // any unknown inputs can't be from us
const CWalletTx& prev = (*mi).second;
@@ -35,33 +35,33 @@ bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) co
if (txin.prevout.n >= prev.tx->vout.size())
return false; // invalid input!
- if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
+ if (!(wallet.IsMine(prev.tx->vout[txin.prevout.n]) & filter))
return false;
}
return true;
}
-CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
+CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter)
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
- LOCK(cs_wallet);
- return ((IsMine(txout) & filter) ? txout.nValue : 0);
+ LOCK(wallet.cs_wallet);
+ return ((wallet.IsMine(txout) & filter) ? txout.nValue : 0);
}
-CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
+CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
{
CAmount nCredit = 0;
for (const CTxOut& txout : tx.vout)
{
- nCredit += GetCredit(txout, filter);
+ nCredit += OutputGetCredit(wallet, txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nCredit;
}
-bool CWallet::IsChange(const CScript& script) const
+bool ScriptIsChange(const CWallet& wallet, const CScript& script)
{
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book
@@ -70,179 +70,177 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
- AssertLockHeld(cs_wallet);
- if (IsMine(script))
+ AssertLockHeld(wallet.cs_wallet);
+ if (wallet.IsMine(script))
{
CTxDestination address;
if (!ExtractDestination(script, address))
return true;
- if (!FindAddressBookEntry(address)) {
+ if (!wallet.FindAddressBookEntry(address)) {
return true;
}
}
return false;
}
-bool CWallet::IsChange(const CTxOut& txout) const
+bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
{
- return IsChange(txout.scriptPubKey);
+ return ScriptIsChange(wallet, txout.scriptPubKey);
}
-CAmount CWallet::GetChange(const CTxOut& txout) const
+CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
- return (IsChange(txout) ? txout.nValue : 0);
+ return (OutputIsChange(wallet, txout) ? txout.nValue : 0);
}
-CAmount CWallet::GetChange(const CTransaction& tx) const
+CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
{
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
CAmount nChange = 0;
for (const CTxOut& txout : tx.vout)
{
- nChange += GetChange(txout);
+ nChange += OutputGetChange(wallet, txout);
if (!MoneyRange(nChange))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nChange;
}
-CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
+static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter, bool recalculate = false)
{
- auto& amount = m_amounts[type];
+ auto& amount = wtx.m_amounts[type];
if (recalculate || !amount.m_cached[filter]) {
- amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
- m_is_cache_empty = false;
+ amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter));
+ wtx.m_is_cache_empty = false;
}
return amount.m_value[filter];
}
-CAmount CWalletTx::GetCredit(const isminefilter& filter) const
+CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
+ if (wallet.IsTxImmatureCoinBase(wtx))
return 0;
CAmount credit = 0;
if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change
- credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
+ credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_SPENDABLE);
}
if (filter & ISMINE_WATCH_ONLY) {
- credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
+ credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_WATCH_ONLY);
}
return credit;
}
-CAmount CWalletTx::GetDebit(const isminefilter& filter) const
+CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{
- if (tx->vin.empty())
+ if (wtx.tx->vin.empty())
return 0;
CAmount debit = 0;
if (filter & ISMINE_SPENDABLE) {
- debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
+ debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_SPENDABLE);
}
if (filter & ISMINE_WATCH_ONLY) {
- debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
+ debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_WATCH_ONLY);
}
return debit;
}
-CAmount CWalletTx::GetChange() const
+CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
{
- if (fChangeCached)
- return nChangeCached;
- nChangeCached = pwallet->GetChange(*tx);
- fChangeCached = true;
- return nChangeCached;
+ if (wtx.fChangeCached)
+ return wtx.nChangeCached;
+ wtx.nChangeCached = TxGetChange(wallet, *wtx.tx);
+ wtx.fChangeCached = true;
+ return wtx.nChangeCached;
}
-CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
+CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache)
{
- if (IsImmatureCoinBase() && IsInMainChain()) {
- return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
+ if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
+ return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
return 0;
}
-CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
+CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache)
{
- if (IsImmatureCoinBase() && IsInMainChain()) {
- return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
+ if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
+ return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
+CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache, const isminefilter& filter)
{
- if (pwallet == nullptr)
- return 0;
-
// Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
+ if (wallet.IsTxImmatureCoinBase(wtx))
return 0;
- if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
- return m_amounts[AVAILABLE_CREDIT].m_value[filter];
+ if (fUseCache && allow_cache && wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_cached[filter]) {
+ return wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_value[filter];
}
- bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+ bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
CAmount nCredit = 0;
- uint256 hashTx = GetHash();
- for (unsigned int i = 0; i < tx->vout.size(); i++)
+ uint256 hashTx = wtx.GetHash();
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
- const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, filter);
+ if (!wallet.IsSpent(hashTx, i) && (allow_used_addresses || !wallet.IsSpentKey(hashTx, i))) {
+ const CTxOut &txout = wtx.tx->vout[i];
+ nCredit += OutputGetCredit(wallet, txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}
if (allow_cache) {
- m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
- m_is_cache_empty = false;
+ wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].Set(filter, nCredit);
+ wtx.m_is_cache_empty = false;
}
return nCredit;
}
-void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
- std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const
+void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
+ std::list<COutputEntry>& listReceived,
+ std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter)
{
nFee = 0;
listReceived.clear();
listSent.clear();
// Compute fee:
- CAmount nDebit = GetDebit(filter);
+ CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
- CAmount nValueOut = tx->GetValueOut();
+ CAmount nValueOut = wtx.tx->GetValueOut();
nFee = nDebit - nValueOut;
}
- LOCK(pwallet->cs_wallet);
+ LOCK(wallet.cs_wallet);
// Sent/received.
- for (unsigned int i = 0; i < tx->vout.size(); ++i)
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
{
- const CTxOut& txout = tx->vout[i];
- isminetype fIsMine = pwallet->IsMine(txout);
+ const CTxOut& txout = wtx.tx->vout[i];
+ isminetype fIsMine = wallet.IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
if (nDebit > 0)
{
// Don't report 'change' txouts
- if (pwallet->IsChange(txout))
+ if (OutputIsChange(wallet, txout))
continue;
}
else if (!(fIsMine & filter))
@@ -253,8 +251,8 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
{
- pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
- this->GetHash().ToString());
+ wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
+ wtx.GetHash().ToString());
address = CNoDestination();
}
@@ -271,16 +269,21 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
}
-bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
+bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
+{
+ return (CachedTxGetDebit(wallet, wtx, filter) > 0);
+}
+
+bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
// Quick answer in most cases
- if (!chain().checkFinalTx(*wtx.tx)) return false;
- int nDepth = wtx.GetDepthInMainChain();
+ if (!wallet.chain().checkFinalTx(*wtx.tx)) return false;
+ int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
// using wtx's cached debit
- if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
+ if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx, ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
if (!wtx.InMempool()) return false;
@@ -289,41 +292,41 @@ bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents
for (const CTxIn& txin : wtx.tx->vin)
{
// Transactions not sent by us: not trusted
- const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
+ const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);
if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
// Check that this specific input being spent is trusted
- if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ if (wallet.IsMine(parentOut) != ISMINE_SPENDABLE) return false;
// If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted
- if (!IsTrusted(*parent, trusted_parents)) return false;
+ if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
}
-bool CWalletTx::IsTrusted() const
+bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
{
std::set<uint256> trusted_parents;
- LOCK(pwallet->cs_wallet);
- return pwallet->IsTrusted(*this, trusted_parents);
+ LOCK(wallet.cs_wallet);
+ return CachedTxIsTrusted(wallet, wtx, trusted_parents);
}
-CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const
+Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
{
Balance ret;
isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
{
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
std::set<uint256> trusted_parents;
- for (const auto& entry : mapWallet)
+ for (const auto& entry : wallet.mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{IsTrusted(wtx, trusted_parents)};
- const int tx_depth{wtx.GetDepthInMainChain()};
- const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
- const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
+ const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
+ const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
+ const CAmount tx_credit_mine{CachedTxGetAvailableCredit(wallet, wtx, /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
+ const CAmount tx_credit_watchonly{CachedTxGetAvailableCredit(wallet, wtx, /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
if (is_trusted && tx_depth >= min_depth) {
ret.m_mine_trusted += tx_credit_mine;
ret.m_watchonly_trusted += tx_credit_watchonly;
@@ -332,43 +335,43 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
ret.m_mine_untrusted_pending += tx_credit_mine;
ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
}
- ret.m_mine_immature += wtx.GetImmatureCredit();
- ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit();
+ ret.m_mine_immature += CachedTxGetImmatureCredit(wallet, wtx);
+ ret.m_watchonly_immature += CachedTxGetImmatureWatchOnlyCredit(wallet, wtx);
}
}
return ret;
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
+std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
{
std::map<CTxDestination, CAmount> balances;
{
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
std::set<uint256> trusted_parents;
- for (const auto& walletEntry : mapWallet)
+ for (const auto& walletEntry : wallet.mapWallet)
{
const CWalletTx& wtx = walletEntry.second;
- if (!IsTrusted(wtx, trusted_parents))
+ if (!CachedTxIsTrusted(wallet, wtx, trusted_parents))
continue;
- if (wtx.IsImmatureCoinBase())
+ if (wallet.IsTxImmatureCoinBase(wtx))
continue;
- int nDepth = wtx.GetDepthInMainChain();
- if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
+ int nDepth = wallet.GetTxDepthInMainChain(wtx);
+ if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1))
continue;
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
CTxDestination addr;
- if (!IsMine(wtx.tx->vout[i]))
+ if (!wallet.IsMine(wtx.tx->vout[i]))
continue;
if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
+ CAmount n = wallet.IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
balances[addr] += n;
}
}
@@ -377,13 +380,13 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
return balances;
}
-std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
+std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
std::set< std::set<CTxDestination> > groupings;
std::set<CTxDestination> grouping;
- for (const auto& walletEntry : mapWallet)
+ for (const auto& walletEntry : wallet.mapWallet)
{
const CWalletTx& wtx = walletEntry.second;
@@ -394,9 +397,9 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
for (const CTxIn& txin : wtx.tx->vin)
{
CTxDestination address;
- if(!IsMine(txin)) /* If this input isn't mine, ignore it */
+ if(!InputIsMine(wallet, txin)) /* If this input isn't mine, ignore it */
continue;
- if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
+ if(!ExtractDestination(wallet.mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
continue;
grouping.insert(address);
any_mine = true;
@@ -406,7 +409,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
if (any_mine)
{
for (const CTxOut& txout : wtx.tx->vout)
- if (IsChange(txout))
+ if (OutputIsChange(wallet, txout))
{
CTxDestination txoutAddr;
if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
@@ -423,7 +426,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
// group lone addrs by themselves
for (const auto& txout : wtx.tx->vout)
- if (IsMine(txout))
+ if (wallet.IsMine(txout))
{
CTxDestination address;
if(!ExtractDestination(txout.scriptPubKey, address))
diff --git a/src/wallet/receive.h b/src/wallet/receive.h
index 8eead32413..b4b311636b 100644
--- a/src/wallet/receive.h
+++ b/src/wallet/receive.h
@@ -10,11 +10,55 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/** Returns whether all of the inputs match the filter */
+bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter);
+
+CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter);
+CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter);
+
+bool ScriptIsChange(const CWallet& wallet, const CScript& script) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+bool OutputIsChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx);
+
+CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
+//! filter decides which addresses will count towards the debit
+CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
+CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx);
+CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true);
+CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache = true);
+// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
+// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
+// having to resolve the issue of member access into incomplete type CWallet.
+CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) NO_THREAD_SAFETY_ANALYSIS;
struct COutputEntry
{
CTxDestination destination;
CAmount amount;
int vout;
};
+void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
+ std::list<COutputEntry>& listReceived,
+ std::list<COutputEntry>& listSent,
+ CAmount& nFee, const isminefilter& filter);
+bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
+bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx);
+
+struct Balance {
+ CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
+ CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
+ CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
+ CAmount m_watchonly_trusted{0};
+ CAmount m_watchonly_untrusted_pending{0};
+ CAmount m_watchonly_immature{0};
+};
+Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = true);
+
+std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet);
+std::set<std::set<CTxDestination>> GetAddressGroupings(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
#endif // BITCOIN_WALLET_RECEIVE_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 916f811f9b..ff9e10c5ad 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -31,7 +31,9 @@
#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/load.h>
+#include <wallet/receive.h>
#include <wallet/rpcwallet.h>
+#include <wallet/spend.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -147,9 +149,10 @@ LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_cr
return *spk_man;
}
-static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniValue& entry)
+static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
{
- int confirms = wtx.GetDepthInMainChain();
+ interfaces::Chain& chain = wallet.chain();
+ int confirms = wallet.GetTxDepthInMainChain(wtx);
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase())
entry.pushKV("generated", true);
@@ -162,12 +165,12 @@ static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniVa
CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time)));
entry.pushKV("blocktime", block_time);
} else {
- entry.pushKV("trusted", wtx.IsTrusted());
+ entry.pushKV("trusted", CachedTxIsTrusted(wallet, wtx));
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
UniValue conflicts(UniValue::VARR);
- for (const uint256& conflict : wtx.GetConflicts())
+ for (const uint256& conflict : wallet.GetTxConflicts(wtx))
conflicts.push_back(conflict.GetHex());
entry.pushKV("walletconflicts", conflicts);
entry.pushKV("time", wtx.GetTxTime());
@@ -423,7 +426,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
bilingual_str error;
CTransactionRef tx;
FeeCalculation fee_calc_out;
- const bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
+ const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
@@ -576,8 +579,8 @@ static RPCHelpMan listaddressgroupings()
LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
- std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
- for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
+ std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
+ for (const std::set<CTxDestination>& grouping : GetAddressGroupings(*pwallet)) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping)
{
@@ -686,7 +689,7 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
CAmount amount = 0;
for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
const CWalletTx& wtx = wtx_pair.second;
- if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wtx.GetDepthInMainChain() < min_depth) {
+ if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wallet.GetTxDepthInMainChain(wtx) < min_depth) {
continue;
}
@@ -826,7 +829,7 @@ static RPCHelpMan getbalance()
bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
- const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
+ const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
},
@@ -851,7 +854,7 @@ static RPCHelpMan getunconfirmedbalance()
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
+ return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
},
};
}
@@ -1085,7 +1088,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool
continue;
}
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth < nMinDepth)
continue;
@@ -1310,9 +1313,9 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
- wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
+ CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine);
- bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
+ bool involvesWatchonly = CachedTxIsFromMe(wallet, wtx, ISMINE_WATCH_ONLY);
// Sent
if (!filter_label)
@@ -1333,14 +1336,14 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(wallet.chain(), wtx, entry);
+ WalletTxToJSON(wallet, wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
}
// Received
- if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
+ if (listReceived.size() > 0 && wallet.GetTxDepthInMainChain(wtx) >= nMinDepth) {
for (const COutputEntry& r : listReceived)
{
std::string label;
@@ -1358,9 +1361,9 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
- if (wtx.GetDepthInMainChain() < 1)
+ if (wallet.GetTxDepthInMainChain(wtx) < 1)
entry.pushKV("category", "orphan");
- else if (wtx.IsImmatureCoinBase())
+ else if (wallet.IsTxImmatureCoinBase(wtx))
entry.pushKV("category", "immature");
else
entry.pushKV("category", "generate");
@@ -1375,7 +1378,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(wallet.chain(), wtx, entry);
+ WalletTxToJSON(wallet, wtx, entry);
ret.push_back(entry);
}
}
@@ -1615,7 +1618,7 @@ static RPCHelpMan listsinceblock()
for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
const CWalletTx& tx = pairWtx.second;
- if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
+ if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1736,16 +1739,16 @@ static RPCHelpMan gettransaction()
}
const CWalletTx& wtx = it->second;
- CAmount nCredit = wtx.GetCredit(filter);
- CAmount nDebit = wtx.GetDebit(filter);
+ CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, filter);
+ CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, filter);
CAmount nNet = nCredit - nDebit;
- CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
+ CAmount nFee = (CachedTxIsFromMe(*pwallet, wtx, filter) ? wtx.tx->GetValueOut() - nDebit : 0);
entry.pushKV("amount", ValueFromAmount(nNet - nFee));
- if (wtx.IsFromMe(filter))
+ if (CachedTxIsFromMe(*pwallet, wtx, filter))
entry.pushKV("fee", ValueFromAmount(nFee));
- WalletTxToJSON(pwallet->chain(), wtx, entry);
+ WalletTxToJSON(*pwallet, wtx, entry);
UniValue details(UniValue::VARR);
ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
@@ -2384,7 +2387,7 @@ static RPCHelpMan getbalances()
LOCK(wallet.cs_wallet);
- const auto bal = wallet.GetBalance();
+ const auto bal = GetBalance(wallet);
UniValue balances{UniValue::VOBJ};
{
UniValue balances_mine{UniValue::VOBJ};
@@ -2394,7 +2397,7 @@ static RPCHelpMan getbalances()
if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
// If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
// the total balance, and then subtract bal to get the reused address balance.
- const auto full_bal = wallet.GetBalance(0, false);
+ const auto full_bal = GetBalance(wallet, 0, false);
balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
}
balances.pushKV("mine", balances_mine);
@@ -2462,7 +2465,7 @@ static RPCHelpMan getwalletinfo()
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
- const auto bal = pwallet->GetBalance();
+ const auto bal = GetBalance(*pwallet);
int64_t kp_oldest = pwallet->GetOldestKeyPoolTime();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
@@ -3058,7 +3061,7 @@ static RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
@@ -3274,7 +3277,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
bilingual_str error;
- if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
}
@@ -3959,7 +3962,7 @@ RPCHelpMan getaddressinfo()
UniValue detail = DescribeWalletAddress(*pwallet, dest);
ret.pushKVs(detail);
- ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
+ ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
if (spk_man) {
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 928335da2b..4a7a268982 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -21,6 +21,11 @@ using interfaces::FoundBlock;
static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100};
+int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig)
+{
+ return CalculateMaximumSignedInputSize(wtx.tx->vout[out], &wallet, use_max_sig);
+}
+
std::string COutput::ToString() const
{
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
@@ -64,33 +69,33 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
}
-void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
vCoins.clear();
CAmount nTotal = 0;
// Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
// a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
- bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
+ bool allow_used_addresses = !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true};
std::set<uint256> trusted_parents;
- for (const auto& entry : mapWallet)
+ for (const auto& entry : wallet.mapWallet)
{
const uint256& wtxid = entry.first;
const CWalletTx& wtx = entry.second;
- if (!chain().checkFinalTx(*wtx.tx)) {
+ if (!wallet.chain().checkFinalTx(*wtx.tx)) {
continue;
}
- if (wtx.IsImmatureCoinBase())
+ if (wallet.IsTxImmatureCoinBase(wtx))
continue;
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth < 0)
continue;
@@ -99,7 +104,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = IsTrusted(wtx, trusted_parents);
+ bool safeTx = CachedTxIsTrusted(wallet, wtx, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -152,28 +157,28 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
continue;
- if (IsLockedCoin(entry.first, i))
+ if (wallet.IsLockedCoin(entry.first, i))
continue;
- if (IsSpent(wtxid, i))
+ if (wallet.IsSpent(wtxid, i))
continue;
- isminetype mine = IsMine(wtx.tx->vout[i]);
+ isminetype mine = wallet.IsMine(wtx.tx->vout[i]);
if (mine == ISMINE_NO) {
continue;
}
- if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
+ if (!allow_used_addresses && wallet.IsSpentKey(wtxid, i)) {
continue;
}
- std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
- vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
+ vCoins.push_back(COutput(wallet, wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@@ -192,13 +197,13 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
}
}
-CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
+CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
{
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(vCoins, coinControl);
+ AvailableCoins(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue;
@@ -207,16 +212,16 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
-const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
+const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransaction& tx, int output)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
const CTransaction* ptx = &tx;
int n = output;
- while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
+ while (OutputIsChange(wallet, ptx->vout[n]) && ptx->vin.size() > 0) {
const COutPoint& prevout = ptx->vin[0].prevout;
- auto it = mapWallet.find(prevout.hash);
- if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
- !IsMine(it->second.tx->vout[prevout.n])) {
+ auto it = wallet.mapWallet.find(prevout.hash);
+ if (it == wallet.mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
+ !wallet.IsMine(it->second.tx->vout[prevout.n])) {
break;
}
ptx = it->second.tx.get();
@@ -225,39 +230,39 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out
return ptx->vout[n];
}
-std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
+std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(availableCoins);
+ AvailableCoins(wallet, availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
- if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
- ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
+ if ((coin.fSpendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
+ ExtractDestination(FindNonChangeParentOutput(wallet, *coin.tx->tx, coin.i).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin));
}
}
std::vector<COutPoint> lockedCoins;
- ListLockedCoins(lockedCoins);
+ wallet.ListLockedCoins(lockedCoins);
// Include watch-only for LegacyScriptPubKeyMan wallets without private keys
- const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ const bool include_watch_only = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
for (const COutPoint& output : lockedCoins) {
- auto it = mapWallet.find(output.hash);
- if (it != mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain();
+ auto it = wallet.mapWallet.find(output.hash);
+ if (it != wallet.mapWallet.end()) {
+ int depth = wallet.GetTxDepthInMainChain(it->second);
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
- IsMine(it->second.tx->vout[output.n]) == is_mine_filter
+ wallet.IsMine(it->second.tx->vout[output.n]) == is_mine_filter
) {
CTxDestination address;
- if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
+ if (ExtractDestination(FindNonChangeParentOutput(wallet, *it->second.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back(
- &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
+ wallet, it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
}
}
}
@@ -266,7 +271,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
return result;
}
-std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const
+std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only)
{
std::vector<OutputGroup> groups_out;
@@ -277,12 +282,12 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
if (!output.fSpendable) continue;
size_t ancestors, descendants;
- chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
CInputCoin input_coin = output.GetInputCoin();
// Make an OutputGroup containing just this output
OutputGroup group{coin_sel_params};
- group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
+ group.Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
// Check the OutputGroup's eligibility. Only add the eligible ones.
if (positive_only && group.GetSelectionAmount() <= 0) continue;
@@ -303,7 +308,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
if (!output.fSpendable) continue;
size_t ancestors, descendants;
- chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
CInputCoin input_coin = output.GetInputCoin();
CScript spk = input_coin.txout.scriptPubKey;
@@ -327,7 +332,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
}
// Add the input_coin to group
- group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
+ group->Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
}
// Now we go through the entire map and pull out the OutputGroups
@@ -352,8 +357,8 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups_out;
}
-bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
- std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const
+bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params)
{
setCoinsRet.clear();
nValueRet = 0;
@@ -363,7 +368,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
std::vector<std::tuple<CAmount, std::set<CInputCoin>, CAmount>> results;
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
- std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */);
+ std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, true /* positive_only */);
std::set<CInputCoin> bnb_coins;
CAmount bnb_value;
if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, bnb_coins, bnb_value)) {
@@ -372,7 +377,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
}
// 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.
- std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */);
+ std::vector<OutputGroup> all_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, false /* positive_only */);
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
std::set<CInputCoin> knapsack_coins;
@@ -397,7 +402,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
return true;
}
-bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const
+bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params)
{
std::vector<COutput> vCoins(vAvailableCoins);
CAmount value_to_select = nTargetValue;
@@ -423,8 +428,8 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
coin_control.ListSelected(vPresetInputs);
for (const COutPoint& outpoint : vPresetInputs)
{
- std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
- if (it != mapWallet.end())
+ std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
+ if (it != wallet.mapWallet.end())
{
const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
@@ -432,7 +437,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return false;
}
// Just to calculate the marginal byte size
- CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false));
+ CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
nValueFromPresetInputs += coin.txout.nValue;
if (coin.m_input_bytes <= 0) {
return false; // Not solvable, can't estimate size for fee
@@ -460,7 +465,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
unsigned int limit_ancestor_count = 0;
unsigned int limit_descendant_count = 0;
- chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
+ wallet.chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
@@ -483,32 +488,32 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
// confirmations on outputs received from other wallets and only spend confirmed change.
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
// possible) if we cannot fund the transaction otherwise.
- if (m_spend_zero_conf_change) {
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
+ if (wallet.m_spend_zero_conf_change) {
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
// in their entirety.
- if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
+ if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
// received from other wallets.
if (coin_control.m_include_unsafe_inputs
- && AttemptSelection(value_to_select,
+ && AttemptSelection(wallet, value_to_select,
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -516,7 +521,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Try with unlimited ancestors/descendants. The transaction will still need to meet
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
- if (!fRejectLongChains && AttemptSelection(value_to_select,
+ if (!fRejectLongChains && AttemptSelection(wallet, value_to_select,
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -595,7 +600,8 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
return locktime;
}
-bool CWallet::CreateTransactionInternal(
+static bool CreateTransactionInternal(
+ CWallet& wallet,
const std::vector<CRecipient>& vecSend,
CTransactionRef& tx,
CAmount& nFeeRet,
@@ -603,22 +609,22 @@ bool CWallet::CreateTransactionInternal(
bilingual_str& error,
const CCoinControl& coin_control,
FeeCalculation& fee_calc_out,
- bool sign)
+ bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
- AssertLockHeld(cs_wallet);
+ AssertLockHeld(wallet.cs_wallet);
CMutableTransaction txNew; // The resulting transaction that we make
- txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
+ txNew.nLockTime = GetLocktimeForNewTransaction(wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
// Set the long term feerate estimate to the wallet's consolidate feerate
- coin_selection_params.m_long_term_feerate = m_consolidate_feerate;
+ coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate;
CAmount recipients_sum = 0;
- const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
- ReserveDestination reservedest(this, change_type);
+ const OutputType change_type = wallet.TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type, vecSend);
+ ReserveDestination reservedest(&wallet, change_type);
unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from
for (const auto& recipient : vecSend) {
recipients_sum += recipient.nAmount;
@@ -662,7 +668,7 @@ bool CWallet::CreateTransactionInternal(
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
// Get size of spending the change output
- int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, &wallet);
// If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
// as lower-bound to allow BnB to do it's thing
if (change_spend_size == -1) {
@@ -672,18 +678,18 @@ bool CWallet::CreateTransactionInternal(
}
// Set discard feerate
- coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+ coin_selection_params.m_discard_feerate = GetDiscardRate(wallet);
// Get the fee rate to use effective values in coin selection
FeeCalculation feeCalc;
- coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ coin_selection_params.m_effective_feerate = GetMinimumFeeRate(wallet, coin_control, &feeCalc);
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
// provided one
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
return false;
}
- if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
+ if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
// eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
@@ -710,7 +716,7 @@ bool CWallet::CreateTransactionInternal(
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
}
- if (IsDust(txout, chain().relayDustFee()))
+ if (IsDust(txout, wallet.chain().relayDustFee()))
{
error = _("Transaction amount too small");
return false;
@@ -724,12 +730,12 @@ bool CWallet::CreateTransactionInternal(
// Get available coins
std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ AvailableCoins(wallet, vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use
CAmount inputs_sum = 0;
std::set<CInputCoin> setCoins;
- if (!SelectCoins(vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params))
+ if (!SelectCoins(wallet, vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params))
{
error = _("Insufficient funds");
return false;
@@ -767,13 +773,13 @@ bool CWallet::CreateTransactionInternal(
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior
// behavior."
- const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
// Calculate the transaction fee
- TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
int nBytes = tx_sizes.vsize;
if (nBytes < 0) {
error = _("Signing transaction failed");
@@ -798,7 +804,7 @@ bool CWallet::CreateTransactionInternal(
txNew.vout.erase(change_position);
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
nBytes = tx_sizes.vsize;
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
}
@@ -835,7 +841,7 @@ bool CWallet::CreateTransactionInternal(
}
// Error if this output is reduced to be below dust
- if (IsDust(txout, chain().relayDustFee())) {
+ if (IsDust(txout, wallet.chain().relayDustFee())) {
if (txout.nValue < 0) {
error = _("The transaction amount is too small to pay the fee");
} else {
@@ -854,7 +860,7 @@ bool CWallet::CreateTransactionInternal(
return false;
}
- if (sign && !SignTransaction(txNew)) {
+ if (sign && !wallet.SignTransaction(txNew)) {
error = _("Signing transaction failed");
return false;
}
@@ -870,14 +876,14 @@ bool CWallet::CreateTransactionInternal(
return false;
}
- if (nFeeRet > m_default_max_tx_fee) {
+ if (nFeeRet > wallet.m_default_max_tx_fee) {
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false;
}
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
- if (!chain().checkChainLimits(tx)) {
+ if (!wallet.chain().checkChainLimits(tx)) {
error = _("Transaction has too long of a mempool chain");
return false;
}
@@ -888,7 +894,7 @@ bool CWallet::CreateTransactionInternal(
reservedest.KeepDestination();
fee_calc_out = feeCalc;
- WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
+ wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
feeCalc.est.pass.start, feeCalc.est.pass.end,
(feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0,
@@ -899,7 +905,8 @@ bool CWallet::CreateTransactionInternal(
return true;
}
-bool CWallet::CreateTransaction(
+bool CreateTransaction(
+ CWallet& wallet,
const std::vector<CRecipient>& vecSend,
CTransactionRef& tx,
CAmount& nFeeRet,
@@ -919,23 +926,23 @@ bool CWallet::CreateTransaction(
return false;
}
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
int nChangePosIn = nChangePosInOut;
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
- bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
+ bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
// try with avoidpartialspends unless it's enabled already
- if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
+ if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
CCoinControl tmp_cc = coin_control;
tmp_cc.m_avoid_partial_spends = true;
CAmount nFeeRet2;
CTransactionRef tx2;
int nChangePosInOut2 = nChangePosIn;
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
- if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
+ if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
// if fee of this alternative one is within the range of the max fee, we use this one
- const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee;
- WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
+ const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
+ wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
if (use_aps) {
tx = tx2;
nFeeRet = nFeeRet2;
@@ -946,7 +953,7 @@ bool CWallet::CreateTransaction(
return res;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
+bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
{
std::vector<CRecipient> vecSend;
@@ -965,11 +972,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
// Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
- LOCK(cs_wallet);
+ LOCK(wallet.cs_wallet);
CTransactionRef tx_new;
FeeCalculation fee_calc_out;
- if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
+ if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
return false;
}
@@ -990,7 +997,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
if (lockUnspents) {
- LockCoin(txin.prevout);
+ wallet.LockCoin(txin.prevout);
}
}
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index 03f9a7c2b5..e39f134dc3 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -9,6 +9,9 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+/** Get the marginal bytes if spending the specified output from this transaction */
+int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig = false);
+
class COutput
{
public:
@@ -43,13 +46,13 @@ public:
*/
bool fSafe;
- COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
+ COutput(const CWallet& wallet, const CWalletTx& wtx, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
{
- tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
+ tx = &wtx; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
// If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1
- if (fSpendable && tx) {
- nInputBytes = tx->GetSpendSize(i, use_max_sig);
+ if (fSpendable) {
+ nInputBytes = GetTxSpendSize(wallet, wtx, i, use_max_sig);
}
}
@@ -61,4 +64,76 @@ public:
}
};
+//Get the marginal bytes of spending the specified output
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
+
+struct TxSize {
+ int64_t vsize{-1};
+ int64_t weight{-1};
+};
+
+/** Calculate the size of the transaction assuming all signatures are max size
+* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
+* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+* be AllInputsMine). */
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+
+/**
+ * populate vCoins with vector of available COutputs.
+ */
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
+
+/**
+ * Find non-change parent output.
+ */
+const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransaction& tx, int output) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/**
+ * Return list of available coins and locked coins grouped by non-change output address.
+ */
+std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only);
+
+/**
+ * Shuffle and select coins until nTargetValue is reached while avoiding
+ * small change; This method is stochastic for some inputs and upon
+ * completion the coin set and corresponding actual target value is
+ * assembled
+ * param@[in] coins Set of UTXOs to consider. These will be categorized into
+ * OutputGroups and filtered using eligibility_filter before
+ * selecting coins.
+ * param@[out] setCoinsRet Populated with the coins selected if successful.
+ * param@[out] nValueRet Used to return the total value of selected coins.
+ */
+bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params);
+
+/**
+ * Select a set of coins such that nValueRet >= nTargetValue and at least
+ * all coins from coin_control are selected; never select unconfirmed coins if they are not ours
+ * param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
+ * coin_control and Coin Selection if successful.
+ * param@[out] nValueRet Total value of selected coins including pre-selected ones
+ * from coin_control and Coin Selection if successful.
+ */
+bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
+ const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/**
+ * Create a new transaction paying the recipients with a set of coins
+ * selected by SelectCoins(); Also create the change output, when needed
+ * @note passing nChangePosInOut as -1 will result in setting a random position
+ */
+bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
+
+/**
+ * Insert additional inputs into the transaction by
+ * calling CreateTransaction();
+ */
+bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
+
#endif // BITCOIN_WALLET_SPEND_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 7b2169a5b6..5d51809241 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -10,6 +10,7 @@
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/coinselection.h>
+#include <wallet/spend.h>
#include <wallet/test/wallet_test_fixture.h>
#include <wallet/wallet.h>
@@ -39,7 +40,7 @@ CoinEligibilityFilter filter_standard_extra(6, 6, 0);
CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
@@ -87,7 +88,7 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
wtx->m_is_cache_empty = false;
}
- COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
+ COutput output(wallet, *wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
}
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
@@ -144,7 +145,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinEligibilityFilter& filter)
{
static std::vector<OutputGroup> static_groups;
- static_groups = testWallet.GroupOutputs(vCoins, coin_selection_params, filter, /* positive_only */false);
+ static_groups = GroupOutputs(testWallet, vCoins, coin_selection_params, filter, /* positive_only */false);
return static_groups;
}
@@ -286,7 +287,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
CoinSet setCoinsRet;
CAmount nValueRet;
empty_wallet();
@@ -316,7 +317,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_control.fAllowOtherInputs = true;
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
- BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
+ BOOST_CHECK(SelectCoins(*wallet, vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
}
}
@@ -653,11 +654,11 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSelectionParams cs_params(/* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
CoinSet out_set;
CAmount out_value = 0;
CCoinControl cc;
- BOOST_CHECK(testWallet.SelectCoins(vCoins, target, out_set, out_value, cc, cs_params));
+ BOOST_CHECK(SelectCoins(testWallet, vCoins, target, out_set, out_value, cc, cs_params));
BOOST_CHECK_GE(out_value, target);
}
}
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 1cefa386b7..8a97f7779d 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -22,12 +22,12 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx1;
s_prev_tx1 >> prev_tx1;
- m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx1));
+ m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(prev_tx1));
CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx2;
s_prev_tx2 >> prev_tx2;
- m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx2));
+ m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(prev_tx2));
// Add scripts
CScript rs1;
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index 8821f680b3..e779b2450f 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -5,6 +5,7 @@
#include <policy/fees.h>
#include <validation.h>
#include <wallet/coincontrol.h>
+#include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -32,7 +33,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
coin_control.m_feerate.emplace(10000);
coin_control.fOverrideFeeRate = true;
FeeCalculation fee_calc;
- BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
+ BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
BOOST_CHECK_EQUAL(tx->vout.size(), 1);
BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
BOOST_CHECK_GT(fee, 0);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index bc5498c88e..5431a38bee 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -21,6 +21,8 @@
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
+#include <wallet/receive.h>
+#include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -103,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height);
- BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
+ BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
}
// Verify ScanForWalletTransactions picks up transactions in both the old
@@ -122,7 +124,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 100 * COIN);
+ BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 100 * COIN);
}
// Prune the older block file.
@@ -148,7 +150,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 50 * COIN);
+ BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 50 * COIN);
}
// Prune the remaining block file.
@@ -173,7 +175,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height);
- BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
+ BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
}
}
@@ -233,7 +235,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
}
@@ -278,7 +280,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
::dumpwallet().HandleRequest(request);
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
@@ -297,7 +299,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(context, wallet);
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
@@ -319,7 +321,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
- CWalletTx wtx(&wallet, m_coinbase_txns.back());
+ CWalletTx wtx(m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
@@ -329,13 +331,13 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0);
+ BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 0);
// Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
+ BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 50*COIN);
}
static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@@ -506,7 +508,7 @@ public:
CCoinControl dummy;
FeeCalculation fee_calc_out;
{
- BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
+ BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
}
wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx;
@@ -528,7 +530,7 @@ public:
std::unique_ptr<CWallet> wallet;
};
-BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
+BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString();
@@ -537,14 +539,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
std::map<CTxDestination, std::vector<COutput>> list;
{
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = ListCoins(*wallet);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
// Check initial balance from one mature coinbase transaction.
- BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance());
+ BOOST_CHECK_EQUAL(50 * COIN, GetAvailableBalance(*wallet));
// Add a transaction creating a change address, and confirm ListCoins still
// returns the coin associated with the change address underneath the
@@ -553,7 +555,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = ListCoins(*wallet);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
@@ -563,7 +565,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(available);
+ AvailableCoins(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -575,14 +577,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(available);
+ AvailableCoins(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
{
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = ListCoins(*wallet);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 131faefe0b..094221adf2 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -17,12 +17,8 @@
#include <list>
#include <vector>
-struct COutputEntry;
-
typedef std::map<std::string, std::string> mapValue_t;
-//Get the marginal bytes of spending the specified output
-int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{
@@ -34,6 +30,7 @@ static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
nOrderPos = atoi64(mapValue["n"]);
}
+
static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
{
if (nOrderPos == -1)
@@ -68,8 +65,6 @@ public:
class CWalletTx
{
private:
- const CWallet* const pwallet;
-
/** Constant used in hashBlock to indicate tx has been abandoned, only used at
* serialization/deserialization to avoid ambiguity with conflicted.
*/
@@ -126,7 +121,6 @@ public:
// memory only
enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
- CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
/**
* This flag is true if all m_amounts caches are empty. This is particularly
@@ -139,9 +133,8 @@ public:
mutable bool fInMempool;
mutable CAmount nChangeCached;
- CWalletTx(const CWallet* wallet, CTransactionRef arg)
- : pwallet(wallet),
- tx(std::move(arg))
+ CWalletTx(CTransactionRef arg)
+ : tx(std::move(arg))
{
Init();
}
@@ -264,72 +257,13 @@ public:
m_is_cache_empty = true;
}
- //! filter decides which addresses will count towards the debit
- CAmount GetDebit(const isminefilter& filter) const;
- CAmount GetCredit(const isminefilter& filter) const;
- CAmount GetImmatureCredit(bool fUseCache = true) const;
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
- // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
- // having to resolve the issue of member access into incomplete type CWallet.
- CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
- CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
- CAmount GetChange() const;
-
- /** Get the marginal bytes if spending the specified output from this transaction */
- int GetSpendSize(unsigned int out, bool use_max_sig = false) const
- {
- return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
- }
-
- void GetAmounts(std::list<COutputEntry>& listReceived,
- std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const;
-
- bool IsFromMe(const isminefilter& filter) const
- {
- return (GetDebit(filter) > 0);
- }
-
/** True if only scriptSigs are different */
bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const;
- bool IsTrusted() const;
int64_t GetTxTime() const;
- /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
- bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
-
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
-
- /**
- * Return depth of transaction in blockchain:
- * <0 : conflicts with a transaction this deep in the blockchain
- * 0 : in memory pool, waiting to be included in a block
- * >=1 : this many blocks deep in the main chain
- */
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
- bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
-
- /**
- * @return number of blocks to maturity for this transaction:
- * 0 : is not a coinbase transaction, or is a mature coinbase transaction
- * >0 : is a coinbase transaction which matures in this many blocks
- */
- int GetBlocksToMaturity() const;
bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
void setAbandoned()
{
@@ -346,7 +280,6 @@ public:
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase() const;
// Disable copying of CWalletTx objects to prevent bugs where instances get
// copied in and out of the mapWallet map, and fields are updated in the
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6f3dcf2afa..70349b2455 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -581,7 +581,7 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
- int depth = mit->second.GetDepthInMainChain();
+ int depth = GetTxDepthInMainChain(mit->second);
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent
}
@@ -900,7 +900,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
}
// Inserts only if not already there, returns tx inserted or tx found
- auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, tx));
+ auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx));
CWalletTx& wtx = (*ret.first).second;
bool fInsertedNew = ret.second;
bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);
@@ -984,7 +984,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx)
{
- const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, nullptr));
+ const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(nullptr));
CWalletTx& wtx = ins.first->second;
if (!fill_wtx(wtx, ins.second)) {
return false;
@@ -1074,7 +1074,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{
LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
- return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
+ return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool();
}
void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@@ -1100,7 +1100,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end());
const CWalletTx& origtx = it->second;
- if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
+ if (GetTxDepthInMainChain(origtx) != 0 || origtx.InMempool()) {
return false;
}
@@ -1113,7 +1113,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain();
+ int currentconfirm = GetTxDepthInMainChain(wtx);
// If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
@@ -1168,7 +1168,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain();
+ int currentconfirm = GetTxDepthInMainChain(wtx);
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
@@ -1698,7 +1698,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = GetTxDepthInMainChain(wtx);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1709,24 +1709,24 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
std::string unused_err_string;
- wtx.SubmitMemoryPoolAndRelay(unused_err_string, false);
+ SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, false);
}
}
-bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
+bool CWallet::SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const
{
// Can't relay if wallet is not broadcasting
- if (!pwallet->GetBroadcastTransactions()) return false;
+ if (!GetBroadcastTransactions()) return false;
// Don't relay abandoned transactions
- if (isAbandoned()) return false;
+ if (wtx.isAbandoned()) return false;
// Don't try to submit coinbase transactions. These would fail anyway but would
// cause log spam.
- if (IsCoinBase()) return false;
+ if (wtx.IsCoinBase()) return false;
// Don't try to submit conflicted or confirmed transactions.
- if (GetDepthInMainChain() != 0) return false;
+ if (GetTxDepthInMainChain(wtx) != 0) return false;
// Submit transaction to mempool for relay
- pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString());
+ WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
@@ -1736,18 +1736,17 @@ bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
// Irrespective of the failure reason, un-marking fInMempool
// out-of-order is incorrect - it should be unmarked when
// TransactionRemovedFromMempool fires.
- bool ret = pwallet->chain().broadcastTransaction(tx, pwallet->m_default_max_tx_fee, relay, err_string);
- fInMempool |= ret;
+ bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
+ wtx.fInMempool |= ret;
return ret;
}
-std::set<uint256> CWalletTx::GetConflicts() const
+std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
{
std::set<uint256> result;
- if (pwallet != nullptr)
{
- uint256 myHash = GetHash();
- result = pwallet->GetConflicts(myHash);
+ uint256 myHash = wtx.GetHash();
+ result = GetConflicts(myHash);
result.erase(myHash);
}
return result;
@@ -1785,11 +1784,11 @@ void CWallet::ResendWalletTransactions()
for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
CWalletTx& wtx = item.second;
// Attempt to rebroadcast all txes more than 5 minutes older than
- // the last block. SubmitMemoryPoolAndRelay() will not rebroadcast
+ // the last block. SubmitTxMemoryPoolAndRelay() will not rebroadcast
// any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
std::string unused_err_string;
- if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
+ if (SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, true)) ++submitted_tx_count;
}
} // cs_wallet
@@ -1977,7 +1976,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
std::string err_string;
- if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) {
+ if (!SubmitTxMemoryPoolAndRelay(wtx, err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
@@ -2192,7 +2191,7 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations
std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) const
{
- LOCK(cs_wallet);
+ AssertLockHeld(cs_wallet);
std::set<CTxDestination> result;
for (const std::pair<const CTxDestination, CAddressBookData>& item : m_address_book)
{
@@ -2911,28 +2910,27 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
m_pre_split = false;
}
-int CWalletTx::GetDepthInMainChain() const
+int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
{
- assert(pwallet != nullptr);
- AssertLockHeld(pwallet->cs_wallet);
- if (isUnconfirmed() || isAbandoned()) return 0;
+ AssertLockHeld(cs_wallet);
+ if (wtx.isUnconfirmed() || wtx.isAbandoned()) return 0;
- return (pwallet->GetLastBlockHeight() - m_confirm.block_height + 1) * (isConflicted() ? -1 : 1);
+ return (GetLastBlockHeight() - wtx.m_confirm.block_height + 1) * (wtx.isConflicted() ? -1 : 1);
}
-int CWalletTx::GetBlocksToMaturity() const
+int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
{
- if (!IsCoinBase())
+ if (!wtx.IsCoinBase())
return 0;
- int chain_depth = GetDepthInMainChain();
+ int chain_depth = GetTxDepthInMainChain(wtx);
assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
}
-bool CWalletTx::IsImmatureCoinBase() const
+bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
{
// note GetBlocksToMaturity is 0 for non-coinbase tx
- return GetBlocksToMaturity() > 0;
+ return GetTxBlocksToMaturity(wtx) > 0;
}
bool CWallet::IsCrypted() const
@@ -3254,12 +3252,13 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes
ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal)
{
+ AssertLockHeld(cs_wallet);
+
if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
WalletLogPrintf("Cannot add WalletDescriptor to a non-descriptor wallet\n");
return nullptr;
}
- LOCK(cs_wallet);
auto spk_man = GetDescriptorScriptPubKeyMan(desc);
if (spk_man) {
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 607af3efb0..2dc9eff712 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,9 +21,7 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
-#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
-#include <wallet/spend.h>
#include <wallet/transaction.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -331,8 +329,6 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
- bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
-
/**
* Catch wallet up to current chain, scanning new blocks, updating the best
* block locator and m_last_block_processed, and registering for
@@ -353,17 +349,6 @@ public:
return *m_database;
}
- /**
- * Select a set of coins such that nValueRet >= nTargetValue and at least
- * all coins from coin_control are selected; never select unconfirmed coins if they are not ours
- * param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
- * coin_control and Coin Selection if successful.
- * param@[out] nValueRet Total value of selected coins including pre-selected ones
- * from coin_control and Coin Selection if successful.
- */
- bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
- const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
-
/** Get a name for this wallet for logging/debugging purposes.
*/
const std::string& GetName() const { return m_name; }
@@ -419,39 +404,40 @@ public:
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
-
- //! check whether we support the named feature
- bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); }
- /**
- * populate vCoins with vector of available COutputs.
- */
- void AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ std::set<uint256> GetTxConflicts(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
/**
- * Return list of available coins and locked coins grouped by non-change output address.
+ * Return depth of transaction in blockchain:
+ * <0 : conflicts with a transaction this deep in the blockchain
+ * 0 : in memory pool, waiting to be included in a block
+ * >=1 : this many blocks deep in the main chain
*/
- std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ int GetTxDepthInMainChain(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
+ bool IsTxInMainChain(const CWalletTx& wtx) const { return GetTxDepthInMainChain(wtx) > 0; }
/**
- * Find non-change parent output.
+ * @return number of blocks to maturity for this transaction:
+ * 0 : is not a coinbase transaction, or is a mature coinbase transaction
+ * >0 : is a coinbase transaction which matures in this many blocks
*/
- const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ int GetTxBlocksToMaturity(const CWalletTx& wtx) const;
+ bool IsTxImmatureCoinBase(const CWalletTx& wtx) const;
- /**
- * Shuffle and select coins until nTargetValue is reached while avoiding
- * small change; This method is stochastic for some inputs and upon
- * completion the coin set and corresponding actual target value is
- * assembled
- * param@[in] coins Set of UTXOs to consider. These will be categorized into
- * OutputGroups and filtered using eligibility_filter before
- * selecting coins.
- * param@[out] setCoinsRet Populated with the coins selected if successful.
- * param@[out] nValueRet Used to return the total value of selected coins.
- */
- bool AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
- std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
+ //! check whether we support the named feature
+ bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); }
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -459,8 +445,6 @@ public:
bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const;
-
/** Display address on an external signer. Returns false if external signer support is not compiled */
bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -546,24 +530,9 @@ public:
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
- struct Balance {
- CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
- CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
- CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
- CAmount m_watchonly_trusted{0};
- CAmount m_watchonly_untrusted_pending{0};
- CAmount m_watchonly_immature{0};
- };
- Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const;
- CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
- /**
- * Insert additional inputs into the transaction by
- * calling CreateTransaction();
- */
- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
/** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Sign the tx given the input coins and sighash. */
@@ -591,12 +560,6 @@ public:
size_t* n_signed = nullptr) const;
/**
- * Create a new transaction paying the recipients with a set of coins
- * selected by SelectCoins(); Also create the change output, when needed
- * @note passing nChangePosInOut as -1 will result in setting a random position
- */
- bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
- /**
* Submit the transaction to the node's mempool and then relay to peers.
* Should be called after CreateTransaction unless you want to abort
* broadcasting the transaction.
@@ -607,6 +570,9 @@ public:
*/
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
+ /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
+ bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
+
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{
std::vector<CTxOut> v_txouts(txouts.size());
@@ -664,10 +630,7 @@ public:
int64_t GetOldestKeyPoolTime() const;
- std::set<std::set<CTxDestination>> GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::map<CTxDestination, CAmount> GetAddressBalances() const;
-
- std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
+ std::set<CTxDestination> GetLabelAddresses(const std::string& label) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Marks all outputs in each one of the destinations dirty, so their cache is
@@ -680,25 +643,16 @@ public:
isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Returns amount of debit if the input matches the
* filter, otherwise returns 0
*/
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
- bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
- /** Returns whether all of the inputs match the filter */
- bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
- CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
- CAmount GetChange(const CTransaction& tx) const;
void chainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet();
@@ -922,7 +876,7 @@ public:
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;
//! Add a descriptor to the wallet, return a ScriptPubKeyMan & associated output type
- ScriptPubKeyMan* AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal);
+ ScriptPubKeyMan* AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
};
/**
@@ -965,18 +919,6 @@ public:
}
};
-struct TxSize {
- int64_t vsize{-1};
- int64_t weight{-1};
-};
-
-/** Calculate the size of the transaction assuming all signatures are max size
-* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
-* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
-* be IsAllFromMe). */
-TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
-TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
-
//! Add wallet name to persistent configuration so it will be loaded on startup.
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 2fabe65a93..03464cd2c8 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -954,7 +954,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
uint256 hash;
ssKey >> hash;
vTxHash.push_back(hash);
- vWtx.emplace_back(nullptr /* wallet */, nullptr /* tx */);
+ vWtx.emplace_back(nullptr /* tx */);
ssValue >> vWtx.back();
}
}
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 6ae866cc07..56f4c98317 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -6,6 +6,7 @@
#include <chain.h>
#include <chainparams.h>
+#include <netbase.h>
#include <node/blockstorage.h>
#include <rpc/server.h>
#include <streams.h>
@@ -73,6 +74,20 @@ static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
return 0;
}
+static bool IsZMQAddressIPV6(const std::string &zmq_address)
+{
+ const std::string tcp_prefix = "tcp://";
+ const size_t tcp_index = zmq_address.rfind(tcp_prefix);
+ const size_t colon_index = zmq_address.rfind(":");
+ if (tcp_index == 0 && colon_index != std::string::npos) {
+ const std::string ip = zmq_address.substr(tcp_prefix.length(), colon_index - tcp_prefix.length());
+ CNetAddr addr;
+ LookupHost(ip, addr, false);
+ if (addr.IsIPv6()) return true;
+ }
+ return false;
+}
+
bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
{
assert(!psocket);
@@ -107,6 +122,15 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
+ // On some systems (e.g. OpenBSD) the ZMQ_IPV6 must not be enabled, if the address to bind isn't IPv6
+ const int enable_ipv6 { IsZMQAddressIPV6(address) ? 1 : 0};
+ rc = zmq_setsockopt(psocket, ZMQ_IPV6, &enable_ipv6, sizeof(enable_ipv6));
+ if (rc != 0) {
+ zmqError("Failed to set ZMQ_IPV6");
+ zmq_close(psocket);
+ return false;
+ }
+
rc = zmq_bind(psocket, address.c_str());
if (rc != 0)
{