aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am16
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include6
-rw-r--r--src/addrdb.cpp121
-rw-r--r--src/addrdb.h35
-rw-r--r--src/addrman.cpp174
-rw-r--r--src/addrman.h111
-rw-r--r--src/banman.cpp33
-rw-r--r--src/banman.h6
-rw-r--r--src/bench/bench.h18
-rw-r--r--src/bench/bench_bitcoin.cpp8
-rw-r--r--src/bench/duplicate_inputs.cpp1
-rw-r--r--src/bench/peer_eviction.cpp156
-rw-r--r--src/bitcoin-cli.cpp14
-rw-r--r--src/bitcoin-tx.cpp3
-rw-r--r--src/bitcoin-util.cpp134
-rw-r--r--src/bitcoin-wallet.cpp10
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/chainparams.cpp2
-rw-r--r--src/chainparams.h11
-rw-r--r--src/chainparamsseeds.h16
-rw-r--r--src/coins.cpp4
-rw-r--r--src/coins.h6
-rw-r--r--src/consensus/params.h38
-rw-r--r--src/consensus/tx_verify.cpp9
-rw-r--r--src/core_read.cpp1
-rw-r--r--src/deploymentinfo.cpp36
-rw-r--r--src/deploymentinfo.h29
-rw-r--r--src/deploymentstatus.cpp17
-rw-r--r--src/deploymentstatus.h55
-rw-r--r--src/external_signer.cpp8
-rw-r--r--src/external_signer.h16
-rw-r--r--src/httpserver.cpp28
-rw-r--r--src/i2p.cpp11
-rw-r--r--src/index/base.cpp1
-rw-r--r--src/init.cpp74
-rw-r--r--src/init.h5
-rw-r--r--src/init/bitcoin-node.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/interfaces/wallet.h4
-rw-r--r--src/key.cpp21
-rw-r--r--src/key.h12
-rw-r--r--src/miner.cpp19
-rw-r--r--src/net.cpp162
-rw-r--r--src/net.h34
-rw-r--r--src/net_processing.cpp155
-rw-r--r--src/netaddress.cpp2
-rw-r--r--src/netaddress.h22
-rw-r--r--src/node/blockstorage.cpp10
-rw-r--r--src/node/blockstorage.h7
-rw-r--r--src/node/coin.cpp1
-rw-r--r--src/node/coinstats.cpp1
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interfaces.cpp66
-rw-r--r--src/node/psbt.cpp6
-rw-r--r--src/node/transaction.cpp1
-rw-r--r--src/outputtype.cpp23
-rw-r--r--src/outputtype.h5
-rw-r--r--src/protocol.h83
-rw-r--r--src/psbt.cpp31
-rw-r--r--src/psbt.h11
-rw-r--r--src/pubkey.cpp4
-rw-r--r--src/pubkey.h10
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoingui.cpp13
-rw-r--r--src/qt/bitcoinstrings.cpp10
-rw-r--r--src/qt/coincontroldialog.cpp16
-rw-r--r--src/qt/createwalletdialog.cpp7
-rw-r--r--src/qt/createwalletdialog.h9
-rw-r--r--src/qt/guiutil.cpp3
-rw-r--r--src/qt/locale/bitcoin_en.ts374
-rw-r--r--src/qt/locale/bitcoin_en.xlf3038
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/overviewpage.cpp32
-rw-r--r--src/qt/overviewpage.h1
-rw-r--r--src/qt/peertablemodel.cpp4
-rw-r--r--src/qt/peertablemodel.h3
-rw-r--r--src/qt/psbtoperationsdialog.cpp6
-rw-r--r--src/qt/qrimagewidget.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp10
-rw-r--r--src/qt/receiverequestdialog.cpp2
-rw-r--r--src/qt/rpcconsole.cpp21
-rw-r--r--src/qt/sendcoinsdialog.cpp8
-rw-r--r--src/qt/sendcoinsentry.cpp4
-rw-r--r--src/qt/signverifymessagedialog.cpp4
-rw-r--r--src/qt/test/apptests.cpp5
-rw-r--r--src/qt/test/wallettests.cpp6
-rw-r--r--src/qt/transactionview.cpp24
-rw-r--r--src/qt/walletcontroller.cpp3
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletframe.h5
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletview.cpp7
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp65
-rw-r--r--src/rpc/mining.cpp13
-rw-r--r--src/rpc/misc.cpp3
-rw-r--r--src/rpc/rawtransaction.cpp9
-rw-r--r--src/script/descriptor.cpp279
-rw-r--r--src/script/descriptor.h23
-rw-r--r--src/script/interpreter.cpp52
-rw-r--r--src/script/interpreter.h11
-rw-r--r--src/script/sign.cpp195
-rw-r--r--src/script/sign.h9
-rw-r--r--src/script/signingprovider.cpp13
-rw-r--r--src/script/signingprovider.h4
-rw-r--r--src/script/standard.cpp198
-rw-r--r--src/script/standard.h54
-rw-r--r--src/test/addrman_tests.cpp161
-rw-r--r--src/test/blockfilter_index_tests.cpp26
-rw-r--r--src/test/coinstatsindex_tests.cpp8
-rw-r--r--src/test/denialofservice_tests.cpp53
-rw-r--r--src/test/descriptor_tests.cpp8
-rw-r--r--src/test/fuzz/addrman.cpp22
-rw-r--r--src/test/fuzz/banman.cpp41
-rw-r--r--src/test/fuzz/base_encode_decode.cpp7
-rw-r--r--src/test/fuzz/coins_view.cpp8
-rw-r--r--src/test/fuzz/crypto.cpp6
-rw-r--r--src/test/fuzz/deserialize.cpp47
-rw-r--r--src/test/fuzz/integer.cpp16
-rw-r--r--src/test/fuzz/key_io.cpp11
-rw-r--r--src/test/fuzz/load_external_block_file.cpp7
-rw-r--r--src/test/fuzz/netaddress.cpp2
-rw-r--r--src/test/fuzz/node_eviction.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp12
-rw-r--r--src/test/fuzz/script.cpp40
-rw-r--r--src/test/fuzz/util.cpp39
-rw-r--r--src/test/fuzz/util.h34
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp7
-rw-r--r--src/test/interfaces_tests.cpp2
-rw-r--r--src/test/key_tests.cpp42
-rw-r--r--src/test/miner_tests.cpp89
-rw-r--r--src/test/net_peer_eviction_tests.cpp524
-rw-r--r--src/test/serfloat_tests.cpp8
-rw-r--r--src/test/streams_tests.cpp2
-rw-r--r--src/test/system_tests.cpp5
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp48
-rw-r--r--src/test/util/mining.cpp4
-rw-r--r--src/test/util/net.cpp25
-rw-r--r--src/test/util/net.h14
-rw-r--r--src/test/util/setup_common.cpp27
-rw-r--r--src/test/validation_block_tests.cpp16
-rw-r--r--src/test/validation_chainstate_tests.cpp2
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp17
-rw-r--r--src/test/validation_flush_tests.cpp25
-rw-r--r--src/test/versionbits_tests.cpp45
-rw-r--r--src/txdb.cpp29
-rw-r--r--src/txdb.h24
-rw-r--r--src/txmempool.cpp3
-rw-r--r--src/util/system.cpp15
-rw-r--r--src/util/system.h4
-rw-r--r--src/validation.cpp493
-rw-r--r--src/validation.h118
-rw-r--r--src/versionbits.cpp33
-rw-r--r--src/versionbits.h36
-rw-r--r--src/versionbitsinfo.cpp18
-rw-r--r--src/versionbitsinfo.h17
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp8
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h9
-rw-r--r--src/wallet/interfaces.cpp10
-rw-r--r--src/wallet/rpcdump.cpp38
-rw-r--r--src/wallet/rpcwallet.cpp56
-rw-r--r--src/wallet/scriptpubkeyman.cpp164
-rw-r--r--src/wallet/scriptpubkeyman.h44
-rw-r--r--src/wallet/spend.cpp5
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp38
-rw-r--r--src/wallet/wallet.cpp165
-rw-r--r--src/wallet/wallet.h27
-rw-r--r--src/wallet/walletdb.cpp55
-rw-r--r--src/wallet/walletdb.h3
-rw-r--r--src/wallet/walletutil.h3
179 files changed, 5823 insertions, 3543 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e2ed70556d..7de5fb36ed 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -145,6 +145,8 @@ BITCOIN_CORE_H = \
core_memusage.h \
cuckoocache.h \
dbwrapper.h \
+ deploymentinfo.h \
+ deploymentstatus.h \
external_signer.h \
flatfile.h \
fs.h \
@@ -272,7 +274,6 @@ BITCOIN_CORE_H = \
validation.h \
validationinterface.h \
versionbits.h \
- versionbitsinfo.h \
wallet/bdb.h \
wallet/coincontrol.h \
wallet/coinselection.h \
@@ -328,6 +329,7 @@ libbitcoin_server_a_SOURCES = \
chain.cpp \
consensus/tx_verify.cpp \
dbwrapper.cpp \
+ deploymentstatus.cpp \
flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
@@ -540,6 +542,7 @@ libbitcoin_common_a_SOURCES = \
compressor.cpp \
core_read.cpp \
core_write.cpp \
+ deploymentinfo.cpp \
external_signer.cpp \
init/common.cpp \
key.cpp \
@@ -561,7 +564,6 @@ libbitcoin_common_a_SOURCES = \
script/sign.cpp \
script/signingprovider.cpp \
script/standard.cpp \
- versionbitsinfo.cpp \
warnings.cpp \
$(BITCOIN_CORE_H)
@@ -812,23 +814,23 @@ clean-local:
check-symbols: $(bin_PROGRAMS)
if TARGET_DARWIN
@echo "Checking macOS dynamic libraries..."
- $(AM_V_at) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif
if TARGET_WINDOWS
@echo "Checking Windows dynamic libraries..."
- $(AM_V_at) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif
-if GLIBC_BACK_COMPAT
+if TARGET_LINUX
@echo "Checking glibc back compat..."
- $(AM_V_at) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
+ $(AM_V_at) CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif
check-security: $(bin_PROGRAMS)
if HARDEN
@echo "Checking binary security..."
- $(AM_V_at) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
endif
libbitcoin_ipc_mpgen_input = \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 56b8ca8ce6..2a8e4a0aac 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -35,6 +35,7 @@ bench_bench_bitcoin_SOURCES = \
bench/mempool_stress.cpp \
bench/nanobench.h \
bench/nanobench.cpp \
+ bench/peer_eviction.cpp \
bench/rpc_blockchain.cpp \
bench/rpc_mempool.cpp \
bench/util_time.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 105d09f730..fc2fd80166 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -35,11 +35,12 @@ BITCOIN_TEST_SUITE = \
$(TEST_UTIL_H)
FUZZ_SUITE_LD_COMMON = \
+ $(LIBTEST_UTIL) \
+ $(LIBTEST_FUZZ) \
$(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
- $(LIBTEST_UTIL) \
- $(LIBTEST_FUZZ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_CLI) \
@@ -160,7 +161,6 @@ BITCOIN_TESTS += \
wallet/test/scriptpubkeyman_tests.cpp
FUZZ_SUITE_LD_COMMON +=\
- $(LIBBITCOIN_WALLET) \
$(SQLITE_LIBS) \
$(BDB_LIBS)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index c376aced10..b8fd019bab 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -11,19 +11,78 @@
#include <cstdint>
#include <hash.h>
#include <logging/timer.h>
+#include <netbase.h>
#include <random.h>
#include <streams.h>
#include <tinyformat.h>
+#include <univalue.h>
+#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)
{
// Write and commit header, data
try {
- CHashWriter hasher(SER_DISK, CLIENT_VERSION);
+ CHashWriter hasher(stream.GetType(), stream.GetVersion());
stream << Params().MessageStart() << data;
hasher << Params().MessageStart() << data;
stream << hasher.GetHash();
@@ -35,7 +94,7 @@ bool SerializeDB(Stream& stream, const Data& data)
}
template <typename Data>
-bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
+bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
{
// Generate random temporary filename
uint16_t randv = 0;
@@ -45,7 +104,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// open temp output file, and associate with CAutoFile
fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile fileout(file, SER_DISK, version);
if (fileout.IsNull()) {
fileout.fclose();
remove(pathTmp);
@@ -106,11 +165,11 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data)
+bool DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
LogPrintf("Missing or invalid file %s\n", path.string());
return false;
@@ -119,18 +178,54 @@ bool DeserializeFileDB(const fs::path& path, Data& data)
}
} // namespace
-CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
+CBanDB::CBanDB(fs::path ban_list_path)
+ : m_banlist_dat(ban_list_path.string() + ".dat"),
+ m_banlist_json(ban_list_path.string() + ".json")
{
}
bool CBanDB::Write(const banmap_t& banSet)
{
- return SerializeFileDB("banlist", m_ban_list_path, banSet);
+ std::vector<std::string> errors;
+ if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
+ return true;
+ }
+
+ for (const auto& err : errors) {
+ error("%s", err);
+ }
+ return false;
}
-bool CBanDB::Read(banmap_t& banSet)
+bool CBanDB::Read(banmap_t& banSet, bool& dirty)
{
- return DeserializeFileDB(m_ban_list_path, banSet);
+ // If the JSON banlist does not exist, then try to read the non-upgraded banlist.dat.
+ if (!fs::exists(m_banlist_json)) {
+ // If this succeeds then we need to flush to disk in order to create the JSON banlist.
+ dirty = true;
+ return DeserializeFileDB(m_banlist_dat, banSet, CLIENT_VERSION);
+ }
+
+ dirty = false;
+
+ std::map<std::string, util::SettingsValue> settings;
+ std::vector<std::string> errors;
+
+ if (!util::ReadSettings(m_banlist_json, settings, errors)) {
+ for (const auto& err : errors) {
+ LogPrintf("Cannot load banlist %s: %s\n", m_banlist_json.string(), err);
+ }
+ return false;
+ }
+
+ try {
+ BanMapFromJson(settings[JSON_KEY], banSet);
+ } catch (const std::runtime_error& e) {
+ LogPrintf("Cannot parse banlist %s: %s\n", m_banlist_json.string(), e.what());
+ return false;
+ }
+
+ return true;
}
CAddrDB::CAddrDB()
@@ -140,12 +235,12 @@ CAddrDB::CAddrDB()
bool CAddrDB::Write(const CAddrMan& addr)
{
- return SerializeFileDB("peers", pathAddr, addr);
+ return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr)
{
- return DeserializeFileDB(pathAddr, addr);
+ return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
@@ -161,13 +256,13 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
{
LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
- SerializeFileDB("anchors", anchors_db_path, anchors);
+ SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
}
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors)) {
+ if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
} else {
anchors.clear();
diff --git a/src/addrdb.h b/src/addrdb.h
index 8953ebb169..399103c991 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -9,6 +9,7 @@
#include <fs.h>
#include <net_types.h> // For banmap_t
#include <serialize.h>
+#include <univalue.h>
#include <string>
#include <vector>
@@ -36,6 +37,13 @@ public:
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);
+
SERIALIZE_METHODS(CBanEntry, obj)
{
uint8_t ban_reason = 2; //! For backward compatibility
@@ -48,6 +56,12 @@ public:
nCreateTime = 0;
nBanUntil = 0;
}
+
+ /**
+ * 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) */
@@ -62,15 +76,30 @@ public:
static bool Read(CAddrMan& addr, CDataStream& ssPeers);
};
-/** Access to the banlist database (banlist.dat) */
+/** Access to the banlist databases (banlist.json and banlist.dat) */
class CBanDB
{
private:
- const fs::path m_ban_list_path;
+ /**
+ * JSON key under which the data is stored in the json database.
+ */
+ static constexpr const char* JSON_KEY = "banned_nets";
+
+ const fs::path m_banlist_dat;
+ const fs::path m_banlist_json;
public:
explicit CBanDB(fs::path ban_list_path);
bool Write(const banmap_t& banSet);
- bool Read(banmap_t& banSet);
+
+ /**
+ * Read the banlist from disk.
+ * @param[out] banSet The loaded list. Set if `true` is returned, otherwise it is left
+ * in an undefined state.
+ * @param[out] dirty Indicates whether the loaded list needs flushing to disk. Set if
+ * `true` is returned, otherwise it is left in an undefined state.
+ * @return true on success
+ */
+ bool Read(banmap_t& banSet, bool& dirty);
};
/**
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 14b412a038..389d106164 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -6,12 +6,15 @@
#include <addrman.h>
#include <hash.h>
+#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
#include <serialize.h>
#include <cmath>
#include <optional>
+#include <unordered_map>
+#include <unordered_set>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -75,14 +78,48 @@ double CAddrInfo::GetChance(int64_t nNow) const
return fChance;
}
+void CAddrMan::RemoveInvalid()
+{
+ for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
+ for (size_t i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ const auto id = vvNew[bucket][i];
+ if (id != -1 && !mapInfo[id].IsValid()) {
+ ClearNew(bucket, i);
+ }
+ }
+ }
+
+ for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
+ for (size_t i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ const auto id = vvTried[bucket][i];
+ if (id == -1) {
+ continue;
+ }
+ const auto& addr_info = mapInfo[id];
+ if (addr_info.IsValid()) {
+ continue;
+ }
+ vvTried[bucket][i] = -1;
+ --nTried;
+ SwapRandom(addr_info.nRandomPos, vRandom.size() - 1);
+ vRandom.pop_back();
+ mapAddr.erase(addr_info);
+ mapInfo.erase(id);
+ m_tried_collisions.erase(id);
+ }
+ }
+}
+
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
- std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
+ AssertLockHeld(cs);
+
+ const auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return nullptr;
if (pnId)
*pnId = (*it).second;
- std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
+ const auto it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return nullptr;
@@ -90,6 +127,8 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
+ AssertLockHeld(cs);
+
int nId = nIdCount++;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
@@ -102,6 +141,8 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
+ AssertLockHeld(cs);
+
if (nRndPos1 == nRndPos2)
return;
@@ -122,6 +163,8 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
void CAddrMan::Delete(int nId)
{
+ AssertLockHeld(cs);
+
assert(mapInfo.count(nId) != 0);
CAddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
@@ -136,6 +179,8 @@ void CAddrMan::Delete(int nId)
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
{
+ AssertLockHeld(cs);
+
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
@@ -151,6 +196,8 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
{
+ AssertLockHeld(cs);
+
// remove the entry from all new buckets
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
int pos = info.GetBucketPosition(nKey, true, bucket);
@@ -199,6 +246,8 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
+ AssertLockHeld(cs);
+
int nId;
nLastGood = nTime;
@@ -265,6 +314,8 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
+ AssertLockHeld(cs);
+
if (!addr.IsRoutable())
return false;
@@ -338,6 +389,8 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -360,7 +413,9 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
CAddrInfo CAddrMan::Select_(bool newOnly)
{
- if (size() == 0)
+ AssertLockHeld(cs);
+
+ if (vRandom.empty())
return CAddrInfo();
if (newOnly && nNew == 0)
@@ -408,8 +463,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{
- std::set<int> setTried;
- std::map<int, int> mapNew;
+ AssertLockHeld(cs);
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
if (vRandom.size() != (size_t)(nTried + nNew))
return -7;
@@ -485,6 +542,8 @@ int CAddrMan::Check_()
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
+ AssertLockHeld(cs);
+
size_t nNodes = vRandom.size();
if (max_pct != 0) {
nNodes = max_pct * nNodes / 100;
@@ -517,6 +576,8 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
void CAddrMan::Connected_(const CService& addr, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -537,6 +598,8 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -555,6 +618,8 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
void CAddrMan::ResolveCollisions_()
{
+ AssertLockHeld(cs);
+
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
int id_new = *it;
@@ -614,6 +679,8 @@ void CAddrMan::ResolveCollisions_()
CAddrInfo CAddrMan::SelectTriedCollision_()
{
+ AssertLockHeld(cs);
+
if (m_tried_collisions.size() == 0) return CAddrInfo();
std::set<int>::iterator it = m_tried_collisions.begin();
@@ -665,3 +732,100 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
}
return bits;
}
+
+void CAddrMan::ResetI2PPorts()
+{
+ for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ const auto id = vvNew[bucket][i];
+ if (id == -1) {
+ continue;
+ }
+ auto it = mapInfo.find(id);
+ if (it == mapInfo.end()) {
+ return;
+ }
+ auto& addr_info = it->second;
+ if (!addr_info.IsI2P() || addr_info.GetPort() == I2P_SAM31_PORT) {
+ continue;
+ }
+
+ auto addr_info_newport = addr_info;
+ // The below changes addr_info_newport.GetKey(), which is used in finding a
+ // bucket and a position within that bucket. So a re-bucketing may be necessary.
+ addr_info_newport.port = I2P_SAM31_PORT;
+
+ // Reposition entries of vvNew within the same bucket because we don't know the source
+ // address which led to the decision to store the entry in vvNew[bucket] so we can't
+ // re-evaluate that decision, but even if we could, CAddrInfo::GetNewBucket() does not
+ // use CAddrInfo::GetKey() so it would end up in the same bucket as before the port
+ // change.
+ const auto i_target = addr_info_newport.GetBucketPosition(nKey, true, bucket);
+
+ if (i_target == i) { // No need to re-position.
+ addr_info = addr_info_newport;
+ continue;
+ }
+
+ // Reposition from i to i_target, removing the entry from i_target (if any).
+ ClearNew(bucket, i_target);
+ vvNew[bucket][i_target] = id;
+ vvNew[bucket][i] = -1;
+ addr_info = addr_info_newport;
+ }
+ }
+
+ for (int bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ const auto id = vvTried[bucket][i];
+ if (id == -1) {
+ continue;
+ }
+ auto it = mapInfo.find(id);
+ if (it == mapInfo.end()) {
+ return;
+ }
+ auto& addr_info = it->second;
+ if (!addr_info.IsI2P() || addr_info.GetPort() == I2P_SAM31_PORT) {
+ continue;
+ }
+
+ auto addr_info_newport = addr_info;
+ // The below changes addr_info_newport.GetKey(), which is used in finding a
+ // bucket and a position within that bucket. So a re-bucketing may be necessary.
+ addr_info_newport.port = I2P_SAM31_PORT;
+
+ const auto bucket_target = addr_info_newport.GetTriedBucket(nKey, m_asmap);
+ const auto i_target = addr_info_newport.GetBucketPosition(nKey, false, bucket_target);
+
+ if (bucket_target == bucket && i_target == i) { // No need to re-position.
+ addr_info = addr_info_newport;
+ continue;
+ }
+
+ // Reposition from (bucket, i) to (bucket_target, i_target). If the latter is
+ // occupied, then move the entry from there to vvNew.
+
+ const auto old_target_id = vvTried[bucket_target][i_target];
+ if (old_target_id != -1) {
+ CAddrInfo& old_target_info = mapInfo[old_target_id];
+
+ old_target_info.fInTried = false;
+ vvTried[bucket_target][i_target] = -1;
+ --nTried;
+
+ const auto new_bucket = old_target_info.GetNewBucket(nKey, m_asmap);
+ const auto new_bucket_i = old_target_info.GetBucketPosition(nKey, true, new_bucket);
+ ClearNew(new_bucket, new_bucket_i);
+
+ old_target_info.nRefCount = 1;
+ vvNew[new_bucket][new_bucket_i] = old_target_id;
+ ++nNew;
+ }
+
+ vvTried[bucket_target][i_target] = id;
+ vvTried[bucket][i] = -1;
+ addr_info = addr_info_newport;
+ }
+ }
+}
diff --git a/src/addrman.h b/src/addrman.h
index 41994288db..2a5c6c06b4 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -8,22 +8,22 @@
#include <clientversion.h>
#include <config/bitcoin-config.h>
+#include <fs.h>
+#include <hash.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 <fs.h>
-#include <hash.h>
#include <iostream>
-#include <map>
#include <optional>
#include <set>
#include <stdint.h>
-#include <streams.h>
+#include <unordered_map>
#include <vector>
/**
@@ -231,6 +231,7 @@ public:
*/
template <typename Stream>
void Serialize(Stream& s_) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -251,7 +252,7 @@ public:
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
- std::map<int, int> mapUnkIds;
+ std::unordered_map<int, int> mapUnkIds;
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
@@ -296,10 +297,11 @@ public:
template <typename Stream>
void Unserialize(Stream& s_)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
- Clear();
+ assert(vRandom.empty());
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
@@ -435,23 +437,28 @@ public:
// Prune new entries with refcount 0 (as a result of collisions).
int nLostUnk = 0;
- for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
+ for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
if (it->second.fInTried == false && it->second.nRefCount == 0) {
- std::map<int, CAddrInfo>::const_iterator itCopy = it++;
+ const auto itCopy = it++;
Delete(itCopy->first);
- nLostUnk++;
+ ++nLostUnk;
} else {
- it++;
+ ++it;
}
}
if (nLost + nLostUnk > 0) {
LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
}
+ RemoveInvalid();
+
+ ResetI2PPorts();
+
Check();
}
void Clear()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
std::vector<int>().swap(vRandom);
@@ -487,26 +494,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
- //! Consistency check
- void Check()
- {
-#ifdef DEBUG_ADDRMAN
- {
- LOCK(cs);
- int err;
- if ((err=Check_()))
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- }
-#endif
- }
-
//! Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
bool fRet = false;
@@ -521,6 +517,7 @@ public:
//! Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
int nAdd = 0;
@@ -536,6 +533,7 @@ public:
//! Mark an entry as accessible.
void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -545,6 +543,7 @@ public:
//! Mark an entry as connection attempted to.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -554,6 +553,7 @@ public:
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void ResolveCollisions()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -563,14 +563,12 @@ public:
//! Randomly select an address in tried that another address is attempting to evict.
CAddrInfo SelectTriedCollision()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo ret;
- {
- LOCK(cs);
- Check();
- ret = SelectTriedCollision_();
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo ret = SelectTriedCollision_();
+ Check();
return ret;
}
@@ -578,14 +576,12 @@ public:
* Choose an address to connect to.
*/
CAddrInfo Select(bool newOnly = false)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo addrRet;
- {
- LOCK(cs);
- Check();
- addrRet = Select_(newOnly);
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo addrRet = Select_(newOnly);
+ Check();
return addrRet;
}
@@ -597,19 +593,19 @@ public:
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
Check();
std::vector<CAddress> vAddr;
- {
- LOCK(cs);
- GetAddr_(vAddr, max_addresses, max_pct, network);
- }
+ GetAddr_(vAddr, max_addresses, max_pct, network);
Check();
return vAddr;
}
//! Outer function for Connected_()
void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -618,6 +614,7 @@ public:
}
void SetServices(const CService &addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -633,8 +630,8 @@ protected:
FastRandomContext insecure_rand;
private:
- //! critical section to protect the inner data structures
- mutable RecursiveMutex cs;
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
//! Serialization versions.
enum Format : uint8_t {
@@ -662,10 +659,10 @@ private:
int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+ std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
std::vector<int> vRandom GUARDED_BY(cs);
@@ -725,6 +722,19 @@ private:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Consistency check
+ void Check()
+ EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+#ifdef DEBUG_ADDRMAN
+ AssertLockHeld(cs);
+ const int err = Check_();
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ }
+#endif
+ }
+
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -756,6 +766,17 @@ private:
//! Update an entry's service bits.
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Remove invalid addresses.
+ void RemoveInvalid() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /**
+ * Reset the ports of I2P peers to 0.
+ * This is needed as a temporary measure because now we enforce port 0 and
+ * only connect to I2P hosts if the port is 0, but in the early days some
+ * I2P addresses with port 8333 were rumoured and persisted into addrmans.
+ */
+ void ResetI2PPorts() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
friend class CAddrManTest;
};
diff --git a/src/banman.cpp b/src/banman.cpp
index bb97fc4809..d2437e6733 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -18,20 +18,18 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
- m_is_dirty = false;
- banmap_t banmap;
- if (m_ban_db.Read(banmap)) {
- SetBanned(banmap); // thread save setter
- SetBannedSetDirty(false); // no need to write down, just read data
- SweepBanned(); // sweep out unused entries
+ if (m_ban_db.Read(m_banned, m_is_dirty)) {
+ SweepBanned(); // sweep out unused entries
- LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
- m_banned.size(), GetTimeMillis() - n_start);
+ LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(),
+ GetTimeMillis() - n_start);
} else {
- LogPrintf("Recreating banlist.dat\n");
- SetBannedSetDirty(true); // force write
- DumpBanlist();
+ LogPrintf("Recreating the banlist database\n");
+ m_banned = {};
+ m_is_dirty = true;
}
+
+ DumpBanlist();
}
BanMan::~BanMan()
@@ -53,8 +51,8 @@ void BanMan::DumpBanlist()
SetBannedSetDirty(false);
}
- LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
- banmap.size(), GetTimeMillis() - n_start);
+ LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(),
+ GetTimeMillis() - n_start);
}
void BanMan::ClearBanned()
@@ -167,13 +165,6 @@ void BanMan::GetBanned(banmap_t& banmap)
banmap = m_banned; //create a thread safe copy
}
-void BanMan::SetBanned(const banmap_t& banmap)
-{
- LOCK(m_cs_banned);
- m_banned = banmap;
- m_is_dirty = true;
-}
-
void BanMan::SweepBanned()
{
int64_t now = GetTime();
@@ -188,7 +179,7 @@ void BanMan::SweepBanned()
m_banned.erase(it++);
m_is_dirty = true;
notify_ui = true;
- LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, sub_net.ToString());
+ LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
} else
++it;
}
diff --git a/src/banman.h b/src/banman.h
index f6bfbd1e49..8c75d4037e 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -17,7 +17,8 @@
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
-// How often to dump addresses to banlist.dat
+
+/// How often to dump banned addresses/subnets to disk.
static constexpr std::chrono::minutes DUMP_BANS_INTERVAL{15};
class CClientUIInterface;
@@ -30,7 +31,7 @@ class CSubNet;
// If an address or subnet is banned, we never accept incoming connections from
// it and never create outgoing connections to it. We won't gossip its address
// to other peers in addr messages. Banned addresses and subnets are stored to
-// banlist.dat on shutdown and reloaded on startup. Banning can be used to
+// disk on shutdown and reloaded on startup. Banning can be used to
// prevent connections with spy nodes or other griefers.
//
// 2. Discouragement. If a peer misbehaves enough (see Misbehaving() in
@@ -79,7 +80,6 @@ public:
void DumpBanlist();
private:
- void SetBanned(const banmap_t& banmap);
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty = true);
diff --git a/src/bench/bench.h b/src/bench/bench.h
index 22f06d8cb8..c4fcd80e33 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -18,16 +18,19 @@
/*
* Usage:
-static void CODE_TO_TIME(benchmark::Bench& bench)
+static void NameOfYourBenchmarkFunction(benchmark::Bench& bench)
{
- ... do any setup needed...
- nanobench::Config().run([&] {
- ... do stuff you want to time...
+ ...do any setup needed...
+
+ bench.run([&] {
+ ...do stuff you want to time; refer to src/bench/nanobench.h
+ for more information and the options that can be passed here...
});
- ... do any cleanup needed...
+
+ ...do any cleanup needed...
}
-BENCHMARK(CODE_TO_TIME);
+BENCHMARK(NameOfYourBenchmarkFunction);
*/
@@ -55,7 +58,8 @@ public:
static void RunAll(const Args& args);
};
-}
+} // namespace benchmark
+
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
#define BENCHMARK(n) \
benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n);
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 135659f87f..aab777cac1 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -16,11 +16,11 @@ static void SetupBenchArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
- argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-asymptote=n1,n2,n3,...", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-asymptote=n1,n2,n3,...", strprintf("Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
// parses a comma separated list like "10,20,30,50"
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 4f6e1122b8..8703a1cf94 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -25,7 +25,6 @@ static void DuplicateInputs(benchmark::Bench& bench)
CMutableTransaction naughtyTx{};
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(testing_setup->m_node.chainman->ActiveChain()));
CBlockIndex* pindexPrev = testing_setup->m_node.chainman->ActiveChain().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
diff --git a/src/bench/peer_eviction.cpp b/src/bench/peer_eviction.cpp
new file mode 100644
index 0000000000..0469f0cb4c
--- /dev/null
+++ b/src/bench/peer_eviction.cpp
@@ -0,0 +1,156 @@
+// 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 <bench/bench.h>
+#include <net.h>
+#include <netaddress.h>
+#include <random.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+static void EvictionProtectionCommon(
+ benchmark::Bench& bench,
+ int num_candidates,
+ std::function<void(NodeEvictionCandidate&)> candidate_setup_fn)
+{
+ using Candidates = std::vector<NodeEvictionCandidate>;
+ FastRandomContext random_context{true};
+ bench.warmup(100).epochIterations(1100);
+
+ Candidates candidates{GetRandomNodeEvictionCandidates(num_candidates, random_context)};
+ for (auto& c : candidates) {
+ candidate_setup_fn(c);
+ }
+
+ std::vector<Candidates> copies{bench.epochs() * bench.epochIterations(), candidates};
+ size_t i{0};
+ bench.run([&] {
+ ProtectEvictionCandidatesByRatio(copies.at(i));
+ ++i;
+ });
+}
+
+/* Benchmarks */
+
+static void EvictionProtection0Networks250Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 250 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_network = NET_IPV4;
+ });
+}
+
+static void EvictionProtection1Networks250Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 250 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = false;
+ if (c.id >= 130 && c.id < 240) { // 110 Tor
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ });
+}
+
+static void EvictionProtection2Networks250Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 250 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = false;
+ if (c.id >= 90 && c.id < 160) { // 70 Tor
+ c.m_network = NET_ONION;
+ } else if (c.id >= 170 && c.id < 250) { // 80 I2P
+ c.m_network = NET_I2P;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ });
+}
+
+static void EvictionProtection3Networks050Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 50 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 28 || c.id == 47); // 2 localhost
+ if (c.id >= 30 && c.id < 47) { // 17 I2P
+ c.m_network = NET_I2P;
+ } else if (c.id >= 24 && c.id < 28) { // 4 Tor
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ });
+}
+
+static void EvictionProtection3Networks100Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 100 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id >= 55 && c.id < 60); // 5 localhost
+ if (c.id >= 70 && c.id < 80) { // 10 I2P
+ c.m_network = NET_I2P;
+ } else if (c.id >= 80 && c.id < 96) { // 16 Tor
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ });
+}
+
+static void EvictionProtection3Networks250Candidates(benchmark::Bench& bench)
+{
+ EvictionProtectionCommon(
+ bench,
+ 250 /* num_candidates */,
+ [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id >= 140 && c.id < 160); // 20 localhost
+ if (c.id >= 170 && c.id < 180) { // 10 I2P
+ c.m_network = NET_I2P;
+ } else if (c.id >= 190 && c.id < 240) { // 50 Tor
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ });
+}
+
+// Candidate numbers used for the benchmarks:
+// - 50 candidates simulates a possible use of -maxconnections
+// - 100 candidates approximates an average node with default settings
+// - 250 candidates is the number of peers reported by operators of busy nodes
+
+// No disadvantaged networks, with 250 eviction candidates.
+BENCHMARK(EvictionProtection0Networks250Candidates);
+
+// 1 disadvantaged network (Tor) with 250 eviction candidates.
+BENCHMARK(EvictionProtection1Networks250Candidates);
+
+// 2 disadvantaged networks (I2P, Tor) with 250 eviction candidates.
+BENCHMARK(EvictionProtection2Networks250Candidates);
+
+// 3 disadvantaged networks (I2P/localhost/Tor) with 50/100/250 eviction candidates.
+BENCHMARK(EvictionProtection3Networks050Candidates);
+BENCHMARK(EvictionProtection3Networks100Candidates);
+BENCHMARK(EvictionProtection3Networks250Candidates);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index c5840b2098..7a5f945511 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -40,6 +40,7 @@ UrlDecodeFn* const URL_DECODE = urlDecode;
static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
+static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
static constexpr int8_t UNKNOWN_NETWORK{-1};
@@ -73,6 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -794,6 +796,9 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
UniValue response(UniValue::VOBJ);
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
+ const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
+ const auto deadline{GetTime<std::chrono::microseconds>() + 1s * timeout};
+
do {
try {
response = CallRPC(rh, strMethod, args, rpcwallet);
@@ -804,11 +809,12 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
}
}
break; // Connection succeeded, no need to retry.
- } catch (const CConnectionFailed&) {
- if (fWait) {
- UninterruptibleSleep(std::chrono::milliseconds{1000});
+ } catch (const CConnectionFailed& e) {
+ const auto now{GetTime<std::chrono::microseconds>()};
+ if (fWait && (timeout <= 0 || now < deadline)) {
+ UninterruptibleSleep(1s);
} else {
- throw;
+ throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
}
}
} while (fWait);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 93ac4b8f7e..3fc87ae1ff 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -506,11 +506,12 @@ static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOut
tx.vout.erase(tx.vout.begin() + outIdx);
}
-static const unsigned int N_SIGHASH_OPTS = 6;
+static const unsigned int N_SIGHASH_OPTS = 7;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
+ {"DEFAULT", SIGHASH_DEFAULT},
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp
index af07b28d3d..f534aecc19 100644
--- a/src/bitcoin-util.cpp
+++ b/src/bitcoin-util.cpp
@@ -7,28 +7,19 @@
#endif
#include <arith_uint256.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <chainparamsbase.h>
#include <clientversion.h>
-#include <coins.h>
-#include <consensus/consensus.h>
#include <core_io.h>
-#include <key_io.h>
-#include <policy/rbf.h>
-#include <primitives/transaction.h>
-#include <script/script.h>
-#include <script/sign.h>
-#include <script/signingprovider.h>
-#include <univalue.h>
-#include <util/moneystr.h>
-#include <util/rbf.h>
-#include <util/strencodings.h>
-#include <util/string.h>
+#include <streams.h>
#include <util/system.h>
#include <util/translation.h>
#include <atomic>
+#include <cstdio>
#include <functional>
#include <memory>
-#include <stdio.h>
#include <thread>
#include <boost/algorithm/string.hpp>
@@ -43,35 +34,29 @@ static void SetupBitcoinUtilArgs(ArgsManager &argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddCommand("grind", "Perform proof of work on hex header string");
+
SetupChainParamsBaseOptions(argsman);
}
// This function returns either one of EXIT_ codes when it's expected to stop the process or
// CONTINUE_EXECUTION when it's expected to continue further.
-static int AppInitUtil(int argc, char* argv[])
+static int AppInitUtil(ArgsManager& args, int argc, char* argv[])
{
- SetupBitcoinUtilArgs(gArgs);
+ SetupBitcoinUtilArgs(args);
std::string error;
- if (!gArgs.ParseParameters(argc, argv, error)) {
+ if (!args.ParseParameters(argc, argv, error)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
return EXIT_FAILURE;
}
- // Check for chain settings (Params() calls are only valid after this clause)
- try {
- SelectParams(gArgs.GetChainName());
- } catch (const std::exception& e) {
- tfm::format(std::cerr, "Error: %s\n", e.what());
- return EXIT_FAILURE;
- }
-
- if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (HelpRequested(args) || args.IsArgSet("-version")) {
// First part of help message is specific to this utility
std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n";
- if (!gArgs.IsArgSet("-version")) {
+ if (!args.IsArgSet("-version")) {
strUsage += "\n"
"Usage: bitcoin-util [options] [commands] Do stuff\n";
- strUsage += "\n" + gArgs.GetHelpMessage();
+ strUsage += "\n" + args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
@@ -82,6 +67,15 @@ static int AppInitUtil(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
+
+ // Check for chain settings (Params() calls are only valid after this clause)
+ try {
+ SelectParams(args.GetChainName());
+ } catch (const std::exception& e) {
+ tfm::format(std::cerr, "Error: %s\n", e.what());
+ return EXIT_FAILURE;
+ }
+
return CONTINUE_EXECUTION;
}
@@ -111,17 +105,17 @@ static void grind_task(uint32_t nBits, CBlockHeader& header_orig, uint32_t offse
}
}
-static int Grind(int argc, char* argv[], std::string& strPrint)
+static int Grind(const std::vector<std::string>& args, std::string& strPrint)
{
- if (argc != 1) {
+ if (args.size() != 1) {
strPrint = "Must specify block header to grind";
- return 1;
+ return EXIT_FAILURE;
}
CBlockHeader header;
- if (!DecodeHexBlockHeader(header, argv[0])) {
+ if (!DecodeHexBlockHeader(header, args[0])) {
strPrint = "Could not decode block header";
- return 1;
+ return EXIT_FAILURE;
}
uint32_t nBits = header.nBits;
@@ -137,49 +131,13 @@ static int Grind(int argc, char* argv[], std::string& strPrint)
}
if (!found) {
strPrint = "Could not satisfy difficulty target";
- return 1;
+ return EXIT_FAILURE;
}
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << header;
strPrint = HexStr(ss);
- return 0;
-}
-
-static int CommandLineUtil(int argc, char* argv[])
-{
- if (argc <= 1) return 1;
-
- std::string strPrint;
- int nRet = 0;
-
- try {
- while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) {
- --argc;
- ++argv;
- }
-
- char* command = argv[1];
- if (strcmp(command, "grind") == 0) {
- nRet = Grind(argc-2, argv+2, strPrint);
- } else {
- strPrint = strprintf("Unknown command %s", command);
- nRet = 1;
- }
- }
- catch (const std::exception& e) {
- strPrint = std::string("error: ") + e.what();
- nRet = EXIT_FAILURE;
- }
- catch (...) {
- PrintExceptionContinue(nullptr, "CommandLineUtil()");
- throw;
- }
-
- if (strPrint != "") {
- tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
- }
- return nRet;
+ return EXIT_SUCCESS;
}
#ifdef WIN32
@@ -193,14 +151,15 @@ __declspec(dllexport) int main(int argc, char* argv[])
int main(int argc, char* argv[])
#endif
{
+ ArgsManager& args = gArgs;
SetupEnvironment();
try {
- int ret = AppInitUtil(argc, argv);
- if (ret != CONTINUE_EXECUTION)
+ int ret = AppInitUtil(args, argc, argv);
+ if (ret != CONTINUE_EXECUTION) {
return ret;
- }
- catch (const std::exception& e) {
+ }
+ } catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitUtil()");
return EXIT_FAILURE;
} catch (...) {
@@ -208,14 +167,29 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
+ const auto cmd = args.GetCommand();
+ if (!cmd) {
+ tfm::format(std::cerr, "Error: must specify a command\n");
+ return EXIT_FAILURE;
+ }
+
int ret = EXIT_FAILURE;
+ std::string strPrint;
try {
- ret = CommandLineUtil(argc, argv);
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "CommandLineUtil()");
+ if (cmd->command == "grind") {
+ ret = Grind(cmd->args, strPrint);
+ } else {
+ assert(false); // unknown command should be caught earlier
+ }
+ } catch (const std::exception& e) {
+ strPrint = std::string("error: ") + e.what();
} catch (...) {
- PrintExceptionContinue(nullptr, "CommandLineUtil()");
+ strPrint = "unknown error";
+ }
+
+ if (strPrint != "") {
+ tfm::format(ret == 0 ? std::cout : std::cerr, "%s\n", strPrint);
}
+
return ret;
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index b84d909b07..765954c92e 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -33,11 +33,11 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddCommand("info", "Get wallet info", OptionsCategory::COMMANDS);
- argsman.AddCommand("create", "Create new wallet file", OptionsCategory::COMMANDS);
- argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", OptionsCategory::COMMANDS);
- argsman.AddCommand("dump", "Print out all of the wallet key-value records", OptionsCategory::COMMANDS);
- argsman.AddCommand("createfromdump", "Create new wallet file from dumped records", OptionsCategory::COMMANDS);
+ argsman.AddCommand("info", "Get wallet info");
+ argsman.AddCommand("create", "Create new wallet file");
+ argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.");
+ argsman.AddCommand("dump", "Print out all of the wallet key-value records");
+ argsman.AddCommand("createfromdump", "Create new wallet file from dumped records");
}
static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index cf9e4fad44..654679af27 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -112,8 +112,8 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
util::ThreadSetInternalName("init");
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
- SetupServerArgs(node);
ArgsManager& args = *Assert(node.args);
+ SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index fdaadeed4a..58a27e053b 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -7,9 +7,9 @@
#include <chainparamsseeds.h>
#include <consensus/merkle.h>
+#include <deploymentinfo.h>
#include <hash.h> // for signet block challenge hash
#include <util/system.h>
-#include <versionbitsinfo.h>
#include <assert.h>
diff --git a/src/chainparams.h b/src/chainparams.h
index 5c2351eea6..4faa6f8d06 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -8,11 +8,13 @@
#include <chainparamsbase.h>
#include <consensus/params.h>
+#include <netaddress.h>
#include <primitives/block.h>
#include <protocol.h>
#include <util/hash_type.h>
#include <memory>
+#include <string>
#include <vector>
typedef std::map<int, uint256> MapCheckpoints;
@@ -80,6 +82,15 @@ public:
const Consensus::Params& GetConsensus() const { return consensus; }
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
uint16_t GetDefaultPort() const { return nDefaultPort; }
+ uint16_t GetDefaultPort(Network net) const
+ {
+ return net == NET_I2P ? I2P_SAM31_PORT : GetDefaultPort();
+ }
+ uint16_t GetDefaultPort(const std::string& addr) const
+ {
+ CNetAddr a;
+ return a.SetSpecial(addr) ? GetDefaultPort(a.GetNetwork()) : GetDefaultPort();
+ }
const CBlock& GenesisBlock() const { return genesis; }
/** Default value for -checkmempool and -checkblockindex argument */
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 08587e2b37..a22529c386 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -683,14 +683,14 @@ static const uint8_t chainparams_seed_main[] = {
0x04,0x20,0x98,0xc6,0x44,0x27,0x90,0x41,0xa6,0x98,0xf9,0x25,0x6c,0x59,0x0f,0x06,0x6d,0x44,0x59,0x0e,0xb2,0x46,0xb0,0xa4,0x37,0x88,0x69,0x8f,0xc1,0x32,0xcd,0x9f,0x15,0xd7,0x20,0x8d,
0x04,0x20,0xaa,0x3a,0x16,0x86,0xea,0x59,0x09,0x04,0x78,0xe5,0x10,0x92,0xe1,0x1d,0xad,0xf7,0x56,0x2b,0xac,0xb0,0x97,0x29,0x63,0x30,0xf4,0x1b,0xcf,0xde,0xf3,0x28,0x0a,0x29,0x20,0x8d,
0x04,0x20,0xbc,0x27,0xae,0x89,0xc1,0x67,0x73,0x0a,0x08,0x02,0xdf,0xb7,0xcc,0x94,0xc7,0x9f,0xf4,0x72,0x7a,0x9b,0x20,0x0c,0x5c,0x11,0x3d,0x22,0xd6,0x13,0x88,0x66,0x74,0xbf,0x20,0x8d,
- 0x05,0x20,0xfe,0x97,0xba,0x09,0x2a,0xa4,0x85,0x10,0xa1,0x04,0x7b,0x88,0x7a,0x5a,0x06,0x53,0x71,0x93,0x3b,0xf9,0xa2,0x2f,0xd9,0xe3,0x8f,0xa5,0xa2,0xac,0x1e,0x6c,0x6c,0x8c,0x20,0x8d,
- 0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x20,0x8d,
- 0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x20,0x8d,
- 0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x20,0x8d,
- 0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x20,0x8d,
- 0x05,0x20,0x7a,0x65,0xf7,0x47,0x42,0x9d,0x66,0x42,0x3b,0xb3,0xa7,0x03,0x6c,0x46,0x78,0x19,0x28,0x78,0x1e,0xa3,0x7c,0x67,0x44,0xb7,0x83,0x05,0xe3,0xfe,0xa5,0xe4,0x0a,0x6e,0x20,0x8d,
- 0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x20,0x8d,
- 0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x20,0x8d,
+ 0x05,0x20,0xfe,0x97,0xba,0x09,0x2a,0xa4,0x85,0x10,0xa1,0x04,0x7b,0x88,0x7a,0x5a,0x06,0x53,0x71,0x93,0x3b,0xf9,0xa2,0x2f,0xd9,0xe3,0x8f,0xa5,0xa2,0xac,0x1e,0x6c,0x6c,0x8c,0x00,0x00,
+ 0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x00,0x00,
+ 0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x00,0x00,
+ 0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x00,0x00,
+ 0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x00,0x00,
+ 0x05,0x20,0x7a,0x65,0xf7,0x47,0x42,0x9d,0x66,0x42,0x3b,0xb3,0xa7,0x03,0x6c,0x46,0x78,0x19,0x28,0x78,0x1e,0xa3,0x7c,0x67,0x44,0xb7,0x83,0x05,0xe3,0xfe,0xa5,0xe4,0x0a,0x6e,0x00,0x00,
+ 0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x00,0x00,
+ 0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x00,0x00,
};
static const uint8_t chainparams_seed_test[] = {
diff --git a/src/coins.cpp b/src/coins.cpp
index d52851cadd..ce0b131de6 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -13,7 +13,7 @@ bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return f
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
-CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
+std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
{
@@ -28,7 +28,7 @@ uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
-CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
+std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {}
diff --git a/src/coins.h b/src/coins.h
index 816b4864a3..3151a260d9 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -180,7 +180,7 @@ public:
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
//! Get a cursor to iterate over the whole state
- virtual CCoinsViewCursor *Cursor() const;
+ virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
//! As we use CCoinsViews polymorphically, have a virtual destructor
virtual ~CCoinsView() {}
@@ -204,7 +204,7 @@ public:
std::vector<uint256> GetHeadBlocks() const override;
void SetBackend(CCoinsView &viewIn);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
- CCoinsViewCursor *Cursor() const override;
+ std::unique_ptr<CCoinsViewCursor> Cursor() const override;
size_t EstimateSize() const override;
};
@@ -237,7 +237,7 @@ public:
uint256 GetBestBlock() const override;
void SetBestBlock(const uint256 &hashBlock);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
- CCoinsViewCursor* Cursor() const override {
+ std::unique_ptr<CCoinsViewCursor> Cursor() const override {
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
}
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 28c95e0884..9205cfee87 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -11,13 +11,27 @@
namespace Consensus {
-enum DeploymentPos
-{
+/**
+ * A buried deployment is one where the height of the activation has been hardcoded into
+ * the client implementation long after the consensus change has activated. See BIP 90.
+ */
+enum BuriedDeployment : int16_t {
+ // buried deployments get negative values to avoid overlap with DeploymentPos
+ DEPLOYMENT_HEIGHTINCB = std::numeric_limits<int16_t>::min(),
+ DEPLOYMENT_CLTV,
+ DEPLOYMENT_DERSIG,
+ DEPLOYMENT_CSV,
+ DEPLOYMENT_SEGWIT,
+};
+constexpr bool ValidDeployment(BuriedDeployment dep) { return DEPLOYMENT_HEIGHTINCB <= dep && dep <= DEPLOYMENT_SEGWIT; }
+
+enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
- // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
+ // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
+constexpr bool ValidDeployment(DeploymentPos dep) { return DEPLOYMENT_TESTDUMMY <= dep && dep <= DEPLOYMENT_TAPROOT; }
/**
* Struct for each individual consensus rule change using BIP9.
@@ -100,7 +114,25 @@ struct Params {
*/
bool signet_blocks{false};
std::vector<uint8_t> signet_challenge;
+
+ int DeploymentHeight(BuriedDeployment dep) const
+ {
+ switch (dep) {
+ case DEPLOYMENT_HEIGHTINCB:
+ return BIP34Height;
+ case DEPLOYMENT_CLTV:
+ return BIP65Height;
+ case DEPLOYMENT_DERSIG:
+ return BIP66Height;
+ case DEPLOYMENT_CSV:
+ return CSVHeight;
+ case DEPLOYMENT_SEGWIT:
+ return SegwitHeight;
+ } // no default case, so the compiler can warn about missing cases
+ return std::numeric_limits<int>::max();
+ }
};
+
} // namespace Consensus
#endif // BITCOIN_CONSENSUS_PARAMS_H
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index f595f16eab..88d8da6ed5 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -20,6 +20,15 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
+
+ // Even if tx.nLockTime isn't satisfied by nBlockHeight/nBlockTime, a
+ // transaction is still considered final if all inputs' nSequence ==
+ // SEQUENCE_FINAL (0xffffffff), in which case nLockTime is ignored.
+ //
+ // Because of this behavior OP_CHECKLOCKTIMEVERIFY/CheckLockTime() will
+ // also check that the spending input's nSequence != SEQUENCE_FINAL,
+ // ensuring that an unsatisfied nLockTime value will actually cause
+ // IsFinalTx() to return false here:
for (const auto& txin : tx.vin) {
if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL))
return false;
diff --git a/src/core_read.cpp b/src/core_read.cpp
index b5fc93886d..6108961010 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -260,6 +260,7 @@ int ParseSighashString(const UniValue& sighash)
int hash_type = SIGHASH_ALL;
if (!sighash.isNull()) {
static std::map<std::string, int> map_sighash_values = {
+ {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
{std::string("NONE"), int(SIGHASH_NONE)},
diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp
new file mode 100644
index 0000000000..030a7806de
--- /dev/null
+++ b/src/deploymentinfo.cpp
@@ -0,0 +1,36 @@
+// Copyright (c) 2016-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 <deploymentinfo.h>
+
+#include <consensus/params.h>
+
+const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
+ {
+ /*.name =*/ "testdummy",
+ /*.gbt_force =*/ true,
+ },
+ {
+ /*.name =*/ "taproot",
+ /*.gbt_force =*/ true,
+ },
+};
+
+std::string DeploymentName(Consensus::BuriedDeployment dep)
+{
+ assert(ValidDeployment(dep));
+ switch (dep) {
+ case Consensus::DEPLOYMENT_HEIGHTINCB:
+ return "bip34";
+ case Consensus::DEPLOYMENT_CLTV:
+ return "bip65";
+ case Consensus::DEPLOYMENT_DERSIG:
+ return "bip66";
+ case Consensus::DEPLOYMENT_CSV:
+ return "csv";
+ case Consensus::DEPLOYMENT_SEGWIT:
+ return "segwit";
+ } // no default case, so the compiler can warn about missing cases
+ return "";
+}
diff --git a/src/deploymentinfo.h b/src/deploymentinfo.h
new file mode 100644
index 0000000000..63d58a7da2
--- /dev/null
+++ b/src/deploymentinfo.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016-2018 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_DEPLOYMENTINFO_H
+#define BITCOIN_DEPLOYMENTINFO_H
+
+#include <consensus/params.h>
+
+#include <string>
+
+struct VBDeploymentInfo {
+ /** Deployment name */
+ const char *name;
+ /** Whether GBT clients can safely ignore this rule in simplified usage */
+ bool gbt_force;
+};
+
+extern const VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS];
+
+std::string DeploymentName(Consensus::BuriedDeployment dep);
+
+inline std::string DeploymentName(Consensus::DeploymentPos pos)
+{
+ assert(Consensus::ValidDeployment(pos));
+ return VersionBitsDeploymentInfo[pos].name;
+}
+
+#endif // BITCOIN_DEPLOYMENTINFO_H
diff --git a/src/deploymentstatus.cpp b/src/deploymentstatus.cpp
new file mode 100644
index 0000000000..9007800421
--- /dev/null
+++ b/src/deploymentstatus.cpp
@@ -0,0 +1,17 @@
+// 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 <deploymentstatus.h>
+
+#include <consensus/params.h>
+#include <versionbits.h>
+
+VersionBitsCache g_versionbitscache;
+
+/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and
+ * ValidDeployment check */
+
+static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)");
+static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)");
+static_assert(!ValidDeployment(static_cast<Consensus::BuriedDeployment>(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)");
diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h
new file mode 100644
index 0000000000..f95c5996f5
--- /dev/null
+++ b/src/deploymentstatus.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_DEPLOYMENTSTATUS_H
+#define BITCOIN_DEPLOYMENTSTATUS_H
+
+#include <chain.h>
+#include <versionbits.h>
+
+#include <limits>
+
+/** Global cache for versionbits deployment status */
+extern VersionBitsCache g_versionbitscache;
+
+/** Determine if a deployment is active for the next block */
+inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep);
+}
+
+inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return ThresholdState::ACTIVE == g_versionbitscache.State(pindexPrev, params, dep);
+}
+
+/** Determine if a deployment is active for this block */
+inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return index.nHeight >= params.DeploymentHeight(dep);
+}
+
+inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return DeploymentActiveAfter(index.pprev, params, dep);
+}
+
+/** Determine if a deployment is enabled (can ever be active) */
+inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::BuriedDeployment dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return params.DeploymentHeight(dep) != std::numeric_limits<int>::max();
+}
+
+inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::DeploymentPos dep)
+{
+ assert(Consensus::ValidDeployment(dep));
+ return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE;
+}
+
+#endif // BITCOIN_DEPLOYMENTSTATUS_H
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index f16d21fa60..d6388b759a 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -13,9 +13,7 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
-ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name): m_command(command), m_fingerprint(fingerprint), m_chain(chain), m_name(name) {}
+ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
const std::string ExternalSigner::NetworkArg() const
{
@@ -55,7 +53,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
- signers.push_back(ExternalSigner(command, fingerprintStr, chain, name));
+ signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}
return true;
}
@@ -116,5 +114,3 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return true;
}
-
-#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/external_signer.h b/src/external_signer.h
index b3b202091a..e40fd7f010 100644
--- a/src/external_signer.h
+++ b/src/external_signer.h
@@ -11,8 +11,6 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
struct PartiallySignedTransaction;
//! Enables interaction with an external signing device or service, such as
@@ -23,24 +21,24 @@ private:
//! The command which handles interaction with the external signer.
std::string m_command;
+ //! Bitcoin mainnet, testnet, etc
+ std::string m_chain;
+
+ const std::string NetworkArg() const;
+
public:
//! @param[in] command the command which handles interaction with the external signer
//! @param[in] fingerprint master key fingerprint of the signer
//! @param[in] chain "main", "test", "regtest" or "signet"
//! @param[in] name device name
- ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name);
+ ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name);
//! Master key fingerprint of the signer
std::string m_fingerprint;
- //! Bitcoin mainnet, testnet, etc
- std::string m_chain;
-
//! Name of signer
std::string m_name;
- const std::string NetworkArg() const;
-
//! Obtain a list of signers. Calls `<command> enumerate`.
//! @param[in] command the command which handles interaction with the external signer
//! @param[in,out] signers vector to which new signers (with a unique master key fingerprint) are added
@@ -65,6 +63,4 @@ public:
bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error);
};
-#endif // ENABLE_EXTERNAL_SIGNER
-
#endif // BITCOIN_EXTERNAL_SIGNER_H
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 45c049c3be..8741ad9b86 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -83,7 +83,7 @@ public:
bool Enqueue(WorkItem* item)
{
LOCK(cs);
- if (queue.size() >= maxDepth) {
+ if (!running || queue.size() >= maxDepth) {
return false;
}
queue.emplace_back(std::unique_ptr<WorkItem>(item));
@@ -99,7 +99,7 @@ public:
WAIT_LOCK(cs, lock);
while (running && queue.empty())
cond.wait(lock);
- if (!running)
+ if (!running && queue.empty())
break;
i = std::move(queue.front());
queue.pop_front();
@@ -136,7 +136,7 @@ static struct evhttp* eventHTTP = nullptr;
//! List of subnets to allow RPC connections from
static std::vector<CSubNet> rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
-static WorkQueue<HTTPClosure>* workQueue = nullptr;
+static std::unique_ptr<WorkQueue<HTTPClosure>> g_work_queue{nullptr};
//! Handlers for (sub)paths
static std::vector<HTTPPathHandler> pathHandlers;
//! Bound listening sockets
@@ -256,10 +256,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
// Dispatch to worker thread
if (i != iend) {
std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler));
- assert(workQueue);
- if (workQueue->Enqueue(item.get()))
+ assert(g_work_queue);
+ if (g_work_queue->Enqueue(item.get())) {
item.release(); /* if true, queue took ownership */
- else {
+ } else {
LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");
}
@@ -392,7 +392,7 @@ bool InitHTTPServer()
int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
- workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
+ g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth);
// transfer ownership to eventBase/HTTP via .release()
eventBase = base_ctr.release();
eventHTTP = http_ctr.release();
@@ -424,7 +424,7 @@ void StartHTTPServer()
g_thread_http = std::thread(ThreadHTTP, eventBase);
for (int i = 0; i < rpcThreads; i++) {
- g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
+ g_thread_http_workers.emplace_back(HTTPWorkQueueRun, g_work_queue.get(), i);
}
}
@@ -435,21 +435,20 @@ void InterruptHTTPServer()
// Reject requests on current connections
evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
}
- if (workQueue)
- workQueue->Interrupt();
+ if (g_work_queue) {
+ g_work_queue->Interrupt();
+ }
}
void StopHTTPServer()
{
LogPrint(BCLog::HTTP, "Stopping HTTP server\n");
- if (workQueue) {
+ if (g_work_queue) {
LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
- for (auto& thread: g_thread_http_workers) {
+ for (auto& thread : g_thread_http_workers) {
thread.join();
}
g_thread_http_workers.clear();
- delete workQueue;
- workQueue = nullptr;
}
// Unlisten sockets, these are what make the event loop running, which means
// that after this and all connections are closed the event loop will quit.
@@ -469,6 +468,7 @@ void StopHTTPServer()
event_base_free(eventBase);
eventBase = nullptr;
}
+ g_work_queue.reset();
LogPrint(BCLog::HTTP, "Stopped HTTP server\n");
}
diff --git a/src/i2p.cpp b/src/i2p.cpp
index 2ae164633b..5e7e42fb77 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -159,7 +159,7 @@ bool Session::Accept(Connection& conn)
const std::string& peer_dest =
conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
- conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
+ conn.peer = CService(DestB64ToAddr(peer_dest), I2P_SAM31_PORT);
return true;
}
@@ -172,6 +172,13 @@ bool Session::Accept(Connection& conn)
bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
{
+ // Refuse connecting to arbitrary ports. We don't specify any destination port to the SAM proxy
+ // when connecting (SAM 3.1 does not use ports) and it forces/defaults it to I2P_SAM31_PORT.
+ if (to.GetPort() != I2P_SAM31_PORT) {
+ proxy_error = false;
+ return false;
+ }
+
proxy_error = true;
std::string session_id;
@@ -366,7 +373,7 @@ void Session::CreateIfNotCreatedAlready()
SendRequestAndGetReply(*sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
session_id, private_key_b64));
- m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
+ m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
m_session_id = session_id;
m_control_sock = std::move(sock);
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3a61af28b7..6fd2701e2e 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -340,7 +340,6 @@ void BaseIndex::Interrupt()
bool BaseIndex::Start(CChainState& active_chainstate)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
m_chainstate = &active_chainstate;
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
diff --git a/src/init.cpp b/src/init.cpp
index 7f64b1acfa..9afd76d62d 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -16,6 +16,7 @@
#include <chain.h>
#include <chainparams.h>
#include <compat/sanity.h>
+#include <deploymentstatus.h>
#include <fs.h>
#include <hash.h>
#include <httprpc.h>
@@ -283,7 +284,7 @@ void Shutdown(NodeContext& node)
init::UnsetGlobals();
node.mempool.reset();
node.fee_estimator.reset();
- node.chainman = nullptr;
+ node.chainman.reset();
node.scheduler.reset();
try {
@@ -347,12 +348,8 @@ static void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void SetupServerArgs(NodeContext& node)
+void SetupServerArgs(ArgsManager& argsman)
{
- assert(!node.args);
- node.args = &gArgs;
- ArgsManager& argsman = *node.args;
-
SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
@@ -445,7 +442,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections.", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1165,7 +1162,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
assert(!node.addrman);
node.addrman = std::make_unique<CAddrMan>();
assert(!node.banman);
- node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
@@ -1179,8 +1176,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
- node.chainman = &g_chainman;
- ChainstateManager& chainman = *Assert(node.chainman);
+ node.chainman = std::make_unique<ChainstateManager>();
+ ChainstateManager& chainman = *node.chainman;
assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
@@ -1352,7 +1349,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
- chainman.InitializeChainstate(*Assert(node.mempool));
+ chainman.InitializeChainstate(Assert(node.mempool.get()));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
@@ -1376,7 +1373,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
- if (!chainman.LoadBlockIndex(chainparams)) {
+ if (!chainman.LoadBlockIndex()) {
if (ShutdownRequested()) break;
strLoadError = _("Error loading block database");
break;
@@ -1385,7 +1382,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!chainman.BlockIndex().empty() &&
- !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@@ -1400,7 +1397,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1431,7 +1428,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!chainstate->ReplayBlocks(chainparams)) {
+ if (!chainstate->ReplayBlocks()) {
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
failed_chainstate_init = true;
break;
@@ -1443,7 +1440,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!is_coinsview_empty(chainstate)) {
// LoadChainTip initializes the chain based on CoinsTip()'s best block
- if (!chainstate->LoadChainTip(chainparams)) {
+ if (!chainstate->LoadChainTip()) {
strLoadError = _("Error initializing block database");
failed_chainstate_init = true;
break; // out of the per-chainstate loop
@@ -1465,7 +1462,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LOCK(cs_main);
auto chainstates{chainman.GetAll()};
if (std::any_of(chainstates.begin(), chainstates.end(),
- [&chainparams](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(chainparams); })) {
+ [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
chainparams.GetConsensus().SegwitHeight);
break;
@@ -1549,21 +1546,21 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
- if (!g_txindex->Start(::ChainstateActive())) {
+ if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
}
}
for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
- if (!GetBlockFilterIndex(filter_type)->Start(::ChainstateActive())) {
+ if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) {
return false;
}
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(/* cache size */ 0, false, fReindex);
- if (!g_coin_stats_index->Start(::ChainstateActive())) {
+ if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) {
return false;
}
}
@@ -1591,7 +1588,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
- if (chainparams.GetConsensus().SegwitHeight != std::numeric_limits<int>::max()) {
+ if (DeploymentEnabled(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
// Advertise witness capabilities.
// The option to not set NODE_WITNESS is only used in the tests and should be removed.
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
@@ -1611,7 +1608,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
- if (::ChainActive().Tip() == nullptr) {
+ if (chainman.ActiveChain().Tip() == nullptr) {
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
@@ -1720,18 +1717,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
return InitError(ResolveErrMsg("bind", bind_arg));
}
- if (connOptions.onion_binds.empty()) {
- connOptions.onion_binds.push_back(DefaultOnionServiceTarget());
- }
-
- if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
- const auto bind_addr = connOptions.onion_binds.front();
- if (connOptions.onion_binds.size() > 1) {
- InitWarning(strprintf(_("More than one onion bind address is provided. Using %s for the automatically created Tor onion service."), bind_addr.ToStringIPPort()));
- }
- StartTorControl(bind_addr);
- }
-
for (const std::string& strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
@@ -1739,6 +1724,27 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.vWhiteBinds.push_back(whitebind);
}
+ // If the user did not specify -bind= or -whitebind= then we bind
+ // on any address - 0.0.0.0 (IPv4) and :: (IPv6).
+ connOptions.bind_on_any = args.GetArgs("-bind").empty() && args.GetArgs("-whitebind").empty();
+
+ CService onion_service_target;
+ if (!connOptions.onion_binds.empty()) {
+ onion_service_target = connOptions.onion_binds.front();
+ } else {
+ onion_service_target = DefaultOnionServiceTarget();
+ connOptions.onion_binds.push_back(onion_service_target);
+ }
+
+ if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
+ if (connOptions.onion_binds.size() > 1) {
+ InitWarning(strprintf(_("More than one onion bind address is provided. Using %s "
+ "for the automatically created Tor onion service."),
+ onion_service_target.ToStringIPPort()));
+ }
+ StartTorControl(onion_service_target);
+ }
+
for (const auto& net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
bilingual_str error;
diff --git a/src/init.h b/src/init.h
index 328eda9c7e..5af6930a16 100644
--- a/src/init.h
+++ b/src/init.h
@@ -20,9 +20,6 @@ struct NodeContext;
namespace interfaces {
struct BlockAndHeaderTipInfo;
}
-namespace boost {
-class thread_group;
-} // namespace boost
/** Interrupt threads */
void Interrupt(NodeContext& node);
@@ -69,7 +66,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info
/**
* Register all arguments with the ArgsManager
*/
-void SetupServerArgs(NodeContext& node);
+void SetupServerArgs(ArgsManager& argsman);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 49684ede83..6b6157c139 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -6,6 +6,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -20,6 +21,7 @@ public:
: m_node(node),
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
{
+ m_node.args = &gArgs;
m_node.init = this;
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 1e17ce4d3c..1d4504c24f 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -4,6 +4,7 @@
#include <interfaces/init.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -14,6 +15,7 @@ class BitcoindInit : public interfaces::Init
public:
BitcoindInit(NodeContext& node) : m_node(node)
{
+ m_node.args = &gArgs;
m_node.init = this;
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 3395741b1b..7cac435e96 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -277,6 +277,9 @@ public:
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
+
+ //! Check if Taproot has activated
+ virtual bool isTaprootActive() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 35b6160cea..77129423db 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -111,10 +111,8 @@ public:
//! Disconnect node by id.
virtual bool disconnectById(NodeId id) = 0;
-#ifdef ENABLE_EXTERNAL_SIGNER
//! List external signers
virtual std::vector<ExternalSigner> externalSigners() = 0;
-#endif
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a0cb2787b7..fb1febc11b 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -198,9 +198,9 @@ public:
virtual TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) = 0;
+ bool& complete) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 5666adebb8..dcad386e77 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -7,10 +7,13 @@
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
+#include <hash.h>
#include <random.h>
#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
#include <secp256k1_recovery.h>
+#include <secp256k1_schnorrsig.h>
static secp256k1_context* secp256k1_context_sign = nullptr;
@@ -258,6 +261,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
+bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256* aux) const
+{
+ assert(sig.size() == 64);
+ secp256k1_keypair keypair;
+ if (!secp256k1_keypair_create(secp256k1_context_sign, &keypair, begin())) return false;
+ if (merkle_root) {
+ secp256k1_xonly_pubkey pubkey;
+ if (!secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, &keypair)) return false;
+ unsigned char pubkey_bytes[32];
+ if (!secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)) return false;
+ uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
+ if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
+ }
+ bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, secp256k1_nonce_function_bip340, aux ? (void*)aux->data() : nullptr);
+ memory_cleanse(&keypair, sizeof(keypair));
+ return ret;
+}
+
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size()))
return false;
diff --git a/src/key.h b/src/key.h
index 3ee49a778b..d47e54800c 100644
--- a/src/key.h
+++ b/src/key.h
@@ -128,6 +128,18 @@ public:
*/
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;
+ /**
+ * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,
+ * optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
+ * aux.
+ *
+ * When merkle_root is not nullptr, this results in a signature with a modified key as
+ * specified in BIP341:
+ * - If merkle_root->IsNull(): key + H_TapTweak(pubkey)*G
+ * - Otherwise: key + H_TapTweak(pubkey || *merkle_root)
+ */
+ bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const;
+
//! Derive BIP32 child key.
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
diff --git a/src/miner.cpp b/src/miner.cpp
index eccddbb04f..d9186a5d6d 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -13,6 +13,7 @@
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
+#include <deploymentstatus.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
@@ -45,15 +46,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- CBlockIndex* prev_block;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman.m_blockman));
- prev_block = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
- }
+ CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -124,12 +117,11 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*m_chainstate.m_chain.Tip()));
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
- pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
+ pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
@@ -146,12 +138,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// This is only needed in case the witness softfork activation is reverted
// (which would require a very deep reorganization).
// Note that the mempool would accept transactions with witness data before
- // IsWitnessEnabled, but we would only ever mine blocks after IsWitnessEnabled
+ // the deployment is active, but we would only ever mine blocks after activation
// unless there is a massive block reorganization with the witness softfork
// not activated.
// TODO: replace this with a call to main to assess validity of a mempool
// transaction (which in most cases can be a no-op).
- fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
+ fIncludeWitness = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);
int nPackagesSelected = 0;
int nDescendantsUpdated = 0;
@@ -184,7 +176,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_chainstate));
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
diff --git a/src/net.cpp b/src/net.cpp
index 6f9f17ed4e..70ba875c4b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -42,6 +42,7 @@
#endif
#include <algorithm>
+#include <array>
#include <cstdint>
#include <functional>
#include <optional>
@@ -401,7 +402,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Resolve
- const uint16_t default_port{Params().GetDefaultPort()};
+ const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
+ Params().GetDefaultPort()};
if (pszDest) {
std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
@@ -841,18 +843,6 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
-static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
-{
- if (a.m_is_local != b.m_is_local) return b.m_is_local;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
-static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
-{
- if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -883,6 +873,26 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
+/**
+ * Sort eviction candidates by network/localhost and connection uptime.
+ * Candidates near the beginning are more likely to be evicted, and those
+ * near the end are more likely to be protected, e.g. less likely to be evicted.
+ * - First, nodes that are not `is_local` and that do not belong to `network`,
+ * sorted by increasing uptime (from most recently connected to connected longer).
+ * - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime.
+ */
+struct CompareNodeNetworkTime {
+ const bool m_is_local;
+ const Network m_network;
+ CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {}
+ bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const
+ {
+ if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local;
+ if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network;
+ return a.nTimeConnected > b.nTimeConnected;
+ };
+};
+
//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
template <typename T, typename Comparator>
static void EraseLastKElements(
@@ -894,40 +904,77 @@ static void EraseLastKElements(
elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
}
-void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
+void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates)
{
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- // To favorise the diversity of our peer connections, reserve up to (half + 2) of
- // these protected spots for onion and localhost peers, if any, even if they're not
- // longest uptime overall. This helps protect tor peers, which tend to be otherwise
+ // To favorise the diversity of our peer connections, reserve up to half of these protected
+ // spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
+ // This helps protect these higher-latency peers that tend to be otherwise
// disadvantaged under our eviction criteria.
- const size_t initial_size = vEvictionCandidates.size();
- size_t total_protect_size = initial_size / 2;
- const size_t onion_protect_size = total_protect_size / 2;
-
- if (onion_protect_size) {
- // Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
- EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_onion; });
- }
-
- const size_t localhost_min_protect_size{2};
- if (onion_protect_size >= localhost_min_protect_size) {
- // Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
- // to localhost peers, sorted by longest uptime, as manually configured
- // hidden services not using `-bind=addr[:port]=onion` will not be detected
- // as inbound onion connections.
- const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
- const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
- EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_local; });
+ const size_t initial_size = eviction_candidates.size();
+ const size_t total_protect_size{initial_size / 2};
+
+ // Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
+ // array members have first opportunity to recover unused slots from the previous iteration.
+ struct Net { bool is_local; Network id; size_t count; };
+ std::array<Net, 3> networks{
+ {{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
+
+ // Count and store the number of eviction candidates per network.
+ for (Net& n : networks) {
+ n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(),
+ [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ }
+ // Sort `networks` by ascending candidate count, to give networks having fewer candidates
+ // the first opportunity to recover unused protected slots from the previous iteration.
+ std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; });
+
+ // Protect up to 25% of the eviction candidates by disadvantaged network.
+ const size_t max_protect_by_network{total_protect_size / 2};
+ size_t num_protected{0};
+
+ while (num_protected < max_protect_by_network) {
+ // Count the number of disadvantaged networks from which we have peers to protect.
+ auto num_networks = std::count_if(networks.begin(), networks.end(), [](const Net& n) { return n.count; });
+ if (num_networks == 0) {
+ break;
+ }
+ const size_t disadvantaged_to_protect{max_protect_by_network - num_protected};
+ const size_t protect_per_network{std::max(disadvantaged_to_protect / num_networks, static_cast<size_t>(1))};
+ // Early exit flag if there are no remaining candidates by disadvantaged network.
+ bool protected_at_least_one{false};
+
+ for (Net& n : networks) {
+ if (n.count == 0) continue;
+ const size_t before = eviction_candidates.size();
+ EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id),
+ protect_per_network, [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ const size_t after = eviction_candidates.size();
+ if (before > after) {
+ protected_at_least_one = true;
+ const size_t delta{before - after};
+ num_protected += delta;
+ if (num_protected >= max_protect_by_network) {
+ break;
+ }
+ n.count -= delta;
+ }
+ }
+ if (!protected_at_least_one) {
+ break;
+ }
}
// Calculate how many we removed, and update our total number of peers that
// we want to protect based on uptime accordingly.
- total_protect_size -= initial_size - vEvictionCandidates.size();
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
+ assert(num_protected == initial_size - eviction_candidates.size());
+ const size_t remaining_to_protect{total_protect_size - num_protected};
+ EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect);
}
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
@@ -944,8 +991,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvict
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
- const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
- EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
[](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
@@ -1024,7 +1070,7 @@ bool CConnman::AttemptToEvictConnection()
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
node->m_prefer_evict, node->addr.IsLocal(),
- node->m_inbound_onion};
+ node->ConnectedThroughNetwork()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -2019,8 +2065,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// from advertising themselves as a service on another host and
// port, causing a DoS attack as nodes around the network attempt
// to connect to it fruitlessly.
- if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
+ if (addr.GetPort() != Params().GetDefaultPort(addr.GetNetwork()) && nTries < 50) {
continue;
+ }
addrConnect = addr;
break;
@@ -2083,7 +2130,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
}
for (const std::string& strAddNode : lAddresses) {
- CService service(LookupNumeric(strAddNode, Params().GetDefaultPort()));
+ CService service(LookupNumeric(strAddNode, Params().GetDefaultPort(strAddNode)));
AddedNodeInfo addedNode{strAddNode, CService(), false, false};
if (service.IsValid()) {
// strAddNode is an IP:port
@@ -2173,6 +2220,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
void CConnman::ThreadMessageHandler()
{
+ FastRandomContext rng;
while (!flagInterruptMsgProc)
{
std::vector<CNode*> vNodesCopy;
@@ -2186,6 +2234,11 @@ void CConnman::ThreadMessageHandler()
bool fMoreWork = false;
+ // Randomize the order in which we process messages from/to our peers.
+ // This prevents attacks in which an attacker exploits having multiple
+ // consecutive connections in the vNodes list.
+ Shuffle(vNodesCopy.begin(), vNodesCopy.end(), rng);
+
for (CNode* pnode : vNodesCopy)
{
if (pnode->fDisconnect)
@@ -2419,30 +2472,25 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags
return true;
}
-bool CConnman::InitBinds(
- const std::vector<CService>& binds,
- const std::vector<NetWhitebindPermissions>& whiteBinds,
- const std::vector<CService>& onion_binds)
+bool CConnman::InitBinds(const Options& options)
{
bool fBound = false;
- for (const auto& addrBind : binds) {
+ for (const auto& addrBind : options.vBinds) {
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::None);
}
- for (const auto& addrBind : whiteBinds) {
+ for (const auto& addrBind : options.vWhiteBinds) {
fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
}
- if (binds.empty() && whiteBinds.empty()) {
+ for (const auto& addr_bind : options.onion_binds) {
+ fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::None);
+ }
+ if (options.bind_on_any) {
struct in_addr inaddr_any;
inaddr_any.s_addr = htonl(INADDR_ANY);
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
}
-
- for (const auto& addr_bind : onion_binds) {
- fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::None);
- }
-
return fBound;
}
@@ -2450,7 +2498,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
{
Init(connOptions);
- if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds, connOptions.onion_binds)) {
+ if (fListen && !InitBinds(connOptions)) {
if (clientInterface) {
clientInterface->ThreadSafeMessageBox(
_("Failed to listen on any port. Use -listen=0 if you want this."),
diff --git a/src/net.h b/src/net.h
index b43916c55e..e1f109a2c4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -768,6 +768,9 @@ public:
std::vector<NetWhitebindPermissions> vWhiteBinds;
std::vector<CService> vBinds;
std::vector<CService> onion_binds;
+ /// True if the user did not specify -bind= or -whitebind= and thus
+ /// we should bind on `0.0.0.0` (IPv4) and `::` (IPv6).
+ bool bind_on_any;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -962,10 +965,7 @@ private:
bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
- bool InitBinds(
- const std::vector<CService>& binds,
- const std::vector<NetWhitebindPermissions>& whiteBinds,
- const std::vector<CService>& onion_binds);
+ bool InitBinds(const Options& options);
void ThreadOpenAddedConnections();
void AddAddrFetch(const std::string& strDest);
@@ -1209,7 +1209,7 @@ struct NodeEvictionCandidate
uint64_t nKeyedNetGroup;
bool prefer_evict;
bool m_is_local;
- bool m_is_onion;
+ Network m_network;
};
/**
@@ -1227,20 +1227,20 @@ struct NodeEvictionCandidate
* longest, to replicate the non-eviction implicit behavior and preclude attacks
* that start later.
*
- * Half of these protected spots (1/4 of the total) are reserved for onion peers
- * connected via our tor control service, if any, sorted by longest uptime, even
- * if they're not longest uptime overall. Any remaining slots of the 1/4 are
- * then allocated to protect localhost peers, if any (or up to 2 localhost peers
- * if no slots remain and 2 or more onion peers were protected), sorted by
- * longest uptime, as manually configured hidden services not using
- * `-bind=addr[:port]=onion` will not be detected as inbound onion connections.
+ * Half of these protected spots (1/4 of the total) are reserved for the
+ * following categories of peers, sorted by longest uptime, even if they're not
+ * longest uptime overall:
+ *
+ * - onion peers connected via our tor control service
+ *
+ * - localhost peers, as manually configured hidden services not using
+ * `-bind=addr[:port]=onion` will not be detected as inbound onion connections
*
- * This helps protect onion peers, which tend to be otherwise disadvantaged
- * under our eviction criteria for their higher min ping times relative to IPv4
- * and IPv6 peers, and favorise the diversity of peer connections.
+ * - I2P peers
*
- * This function was extracted from SelectNodeToEvict() to be able to test the
- * ratio-based protection logic deterministically.
+ * This helps protect these privacy network peers, which tend to be otherwise
+ * disadvantaged under our eviction criteria for their higher min ping times
+ * relative to IPv4/IPv6 peers, and favorise the diversity of peer connections.
*/
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 65224b4259..315d2ac5cd 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -11,6 +11,7 @@
#include <blockfilter.h>
#include <chainparams.h>
#include <consensus/validation.h>
+#include <deploymentstatus.h>
#include <hash.h>
#include <index/blockfilterindex.h>
#include <merkleblock.h>
@@ -159,10 +160,10 @@ static constexpr size_t MAX_ADDR_TO_SEND{1000};
namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ /** BlockIndex. We must have this since we only request blocks when we've already validated the header. */
+ const CBlockIndex* pindex;
+ /** Optional, used for CMPCTBLOCK downloads */
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
};
/**
@@ -463,16 +464,20 @@ private:
Mutex m_recent_confirmed_transactions_mutex;
std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /* Returns a bool indicating whether we requested this block.
- * Also used if a block was /not/ received and timed out or started with another peer
+ /** Have we requested this block from a peer */
+ bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Remove this block from our tracked requested blocks. Called if:
+ * - the block has been received from a peer
+ * - the request for the block has timed out
*/
- bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/* Mark a block as in flight
* Returns false, still setting pit, if the block was already in flight from the same peer
* pit will only be valid as long as the same cs_main lock is being held
*/
- bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -512,7 +517,7 @@ private:
std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
/** Storage for orphan information */
TxOrphanage m_orphanage;
@@ -627,7 +632,6 @@ struct CNodeState {
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
std::chrono::microseconds m_downloading_since{0us};
int nBlocksInFlight{0};
- int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
@@ -758,32 +762,42 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
+bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- assert(state != nullptr);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->m_stalling_since = 0us;
- mapBlocksInFlight.erase(itInFlight);
- return true;
+ return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
+}
+
+void PeerManagerImpl::RemoveBlockRequest(const uint256& hash)
+{
+ auto it = mapBlocksInFlight.find(hash);
+ if (it == mapBlocksInFlight.end()) {
+ // Block was not requested
+ return;
}
- return false;
+
+ auto [node_id, list_it] = it->second;
+ CNodeState *state = State(node_id);
+ assert(state != nullptr);
+
+ if (state->vBlocksInFlight.begin() == list_it) {
+ // First block on the queue was received, update the start download time for the next one
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
+ }
+ state->vBlocksInFlight.erase(list_it);
+
+ state->nBlocksInFlight--;
+ if (state->nBlocksInFlight == 0) {
+ // Last validated block on the queue was received.
+ m_peers_downloading_from--;
+ }
+ state->m_stalling_since = 0us;
+ mapBlocksInFlight.erase(it);
}
-bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit)
+bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit)
{
+ const uint256& hash{block.GetBlockHash()};
+
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -797,22 +811,20 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
}
// Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
+ RemoveBlockRequest(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
+ {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
state->m_downloading_since = GetTime<std::chrono::microseconds>();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
- nPeersWithValidatedDownloads++;
+ m_peers_downloading_from++;
}
itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
+ if (pit) {
*pit = &itInFlight->second.second;
+ }
return true;
}
@@ -825,12 +837,27 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
return;
}
if (nodestate->fProvidesHeaderAndIDs) {
+ int num_outbound_hb_peers = 0;
for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
if (*it == nodeid) {
lNodesAnnouncingHeaderAndIDs.erase(it);
lNodesAnnouncingHeaderAndIDs.push_back(nodeid);
return;
}
+ CNodeState *state = State(*it);
+ if (state != nullptr && !state->m_is_inbound) ++num_outbound_hb_peers;
+ }
+ if (nodestate->m_is_inbound) {
+ // If we're adding an inbound HB peer, make sure we're not removing
+ // our last outbound HB peer in the process.
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3 && num_outbound_hb_peers == 1) {
+ CNodeState *remove_node = State(lNodesAnnouncingHeaderAndIDs.front());
+ if (remove_node != nullptr && !remove_node->m_is_inbound) {
+ // Put the HB outbound peer in the second slot, so that it
+ // doesn't get removed.
+ std::swap(lNodesAnnouncingHeaderAndIDs.front(), *std::next(lNodesAnnouncingHeaderAndIDs.begin()));
+ }
+ }
}
m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
@@ -971,14 +998,14 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
// We consider the chain that this peer is on invalid.
return;
}
- if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
+ if (!State(nodeid)->fHaveWitness && DeploymentActiveAt(*pindex, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
// We wouldn't download this block or its descendants from this peer.
return;
}
if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ } else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window.
@@ -1129,13 +1156,13 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
nSyncStarted--;
for (const QueuedBlock& entry : state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
+ mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
+ m_peers_downloading_from -= (state->nBlocksInFlight != 0);
+ assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0);
m_wtxid_relay_peers -= state->m_wtxid_relay;
@@ -1147,7 +1174,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
// Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
+ assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0);
assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
@@ -1350,7 +1377,6 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
- assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -1442,7 +1468,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
return;
nHighestFastAnnounce = pindex->nHeight;
- bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus());
+ bool fWitnessEnabled = DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);
uint256 hashBlock(pblock->GetHash());
{
@@ -1684,7 +1710,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
BlockValidationState state;
- if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -2056,8 +2082,8 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
+ !IsBlockRequested(pindexWalk->GetBlockHash()) &&
+ (!DeploymentActiveAt(*pindexWalk, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -2081,7 +2107,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pfrom.GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2827,7 +2853,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
// it's probably for a re-org. The final block hash
@@ -2910,7 +2936,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
a_recent_block = most_recent_block;
}
BlockValidationState state;
- if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -3372,7 +3398,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
+ if (DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
return;
@@ -3384,7 +3410,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!BlockRequested(pfrom.GetId(), *pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -3397,7 +3423,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3492,7 +3518,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// process from some other peer. We do this after calling
// ProcessNewBlock so that a malleated cmpctblock announcement
// can't be used to interfere with block relay.
- MarkBlockAsReceived(pblock->GetHash());
+ RemoveBlockRequest(pblock->GetHash());
}
}
return;
@@ -3524,7 +3550,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3550,7 +3576,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
// updated, etc.
- MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ RemoveBlockRequest(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
// mapBlockSource is used for potentially punishing peers and
// updating which peers send us compact blocks, so the race
@@ -3615,9 +3641,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
- // Also always process if we requested the block explicitly, as we may
- // need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived(hash);
+ // Always process the block if we requested it, since we may
+ // need it even when it's not a candidate for a new best tip.
+ forceProcessing = IsBlockRequested(hash);
+ RemoveBlockRequest(hash);
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
@@ -4712,9 +4739,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// to unreasonably increase our timeout.
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;
if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.pindex->GetBlockHash().ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -4767,7 +4794,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pto->GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 1ea3969978..e7b3377475 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -489,7 +489,7 @@ bool CNetAddr::IsValid() const
*/
bool CNetAddr::IsRoutable() const
{
- return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal());
+ return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || IsRFC4193() || IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal());
}
/**
diff --git a/src/netaddress.h b/src/netaddress.h
index e0d056b626..5e1d9d2a6f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -11,7 +11,9 @@
#include <attributes.h>
#include <compat.h>
+#include <crypto/siphash.h>
#include <prevector.h>
+#include <random.h>
#include <serialize.h>
#include <tinyformat.h>
#include <util/strencodings.h>
@@ -110,6 +112,9 @@ static constexpr size_t ADDR_CJDNS_SIZE = 16;
/// Size of "internal" (NET_INTERNAL) address (in bytes).
static constexpr size_t ADDR_INTERNAL_SIZE = 10;
+/// SAM 3.1 and earlier do not support specifying ports and force the port to 0.
+static constexpr uint16_t I2P_SAM31_PORT{0};
+
/**
* Network address.
*/
@@ -251,6 +256,7 @@ class CNetAddr
}
}
+ friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -464,6 +470,22 @@ class CNetAddr
}
};
+class CNetAddrHash
+{
+public:
+ size_t operator()(const CNetAddr& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
+};
+
class CSubNet
{
protected:
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 6c66c565ad..0083b74b33 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -248,7 +248,6 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
@@ -494,7 +493,6 @@ struct CImportingNow {
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
{
- const CChainParams& chainparams = Params();
ScheduleBatchPriority();
{
@@ -513,7 +511,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
break; // This error is logged in OpenBlockFile
}
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
- chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file, &pos);
+ chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos);
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exit %s\n", __func__);
return;
@@ -524,7 +522,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
- chainman.ActiveChainstate().LoadGenesisBlock(chainparams);
+ chainman.ActiveChainstate().LoadGenesisBlock();
}
// -loadblock=
@@ -532,7 +530,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
FILE* file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
- chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file);
+ chainman.ActiveChainstate().LoadExternalBlockFile(file);
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exit %s\n", __func__);
return;
@@ -549,7 +547,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
// the relevant pointers before the ABC call.
for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
- if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
+ if (!chainstate->ActivateBestChain(state, nullptr)) {
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
StartShutdown();
return;
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index faf99dea81..7c7bf68178 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -5,12 +5,13 @@
#ifndef BITCOIN_NODE_BLOCKSTORAGE_H
#define BITCOIN_NODE_BLOCKSTORAGE_H
-#include <cstdint>
-#include <vector>
-
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <atomic>
+#include <cstdint>
+#include <vector>
+
class ArgsManager;
class BlockValidationState;
class CBlock;
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index 23d4fa2aae..50fddf3ab0 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -13,7 +13,6 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
assert(node.mempool);
assert(node.chainman);
LOCK2(cs_main, node.mempool->cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 38c1d29250..67e497c218 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -97,7 +97,6 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
if (!pindex) {
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
}
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 6d22a6b110..9afadd09a9 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -12,6 +12,7 @@
#include <policy/fees.h>
#include <scheduler.h>
#include <txmempool.h>
+#include <validation.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index 06adb33a80..135f9ea1c6 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -44,7 +44,7 @@ struct NodeContext {
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
std::unique_ptr<PeerManager> peerman;
- ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
+ std::unique_ptr<ChainstateManager> chainman;
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 171f15d4fb..183b5a5d91 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -6,6 +6,8 @@
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
+#include <deploymentstatus.h>
+#include <external_signer.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
@@ -170,16 +172,24 @@ public:
}
return false;
}
-#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> externalSigners() override
{
+#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> signers = {};
const std::string command = gArgs.GetArg("-signer", "");
if (command == "") return signers;
ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
return signers;
+#else
+ // This result is indistinguishable from a successful call that returns
+ // no signers. For the current GUI this doesn't matter, because the wallet
+ // creation dialog disables the external signer checkbox in both
+ // cases. The return type could be changed to std::optional<std::vector>
+ // (or something that also includes error messages) if this distinction
+ // becomes important.
+ return {};
+#endif // ENABLE_EXTERNAL_SIGNER
}
-#endif
int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; }
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
@@ -197,26 +207,16 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return chainman().ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
- tip = chainman().ActiveChain().Tip();
- }
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
@@ -227,22 +227,12 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
tip = chainman().ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &m_context->chainman->ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
@@ -269,7 +259,6 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainman().ActiveChainstate()));
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
@@ -476,14 +465,12 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
}
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
@@ -493,7 +480,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
@@ -506,7 +492,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -518,9 +503,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -529,9 +512,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
@@ -542,7 +523,6 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
@@ -555,7 +535,6 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
@@ -647,16 +626,7 @@ public:
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &chainman().ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
@@ -719,6 +689,12 @@ public:
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
+ bool isTaprootActive() const override
+ {
+ LOCK(::cs_main);
+ const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
+ return DeploymentActiveAfter(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT);
+ }
NodeContext& m_node;
};
} // namespace
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index c189018268..b013b6d579 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -23,6 +23,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
result.inputs.resize(psbtx.tx->vin.size());
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
PSBTInputAnalysis& input_analysis = result.inputs[i];
@@ -61,7 +63,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Figure out what is missing
SignatureData outdata;
- bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
// Things are missing
if (!complete) {
@@ -121,7 +123,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
Coin newcoin;
- if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !psbtx.GetInputUTXO(newcoin.out, i)) {
success = false;
break;
} else {
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index a1e7a71e2c..f21b390915 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -40,7 +40,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
assert(node.chainman);
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index d96fb282c5..8ede7b9974 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -18,6 +18,7 @@
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
+static const std::string OUTPUT_TYPE_STRING_BECH32M = "bech32m";
bool ParseOutputType(const std::string& type, OutputType& output_type)
{
@@ -30,6 +31,9 @@ bool ParseOutputType(const std::string& type, OutputType& output_type)
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
output_type = OutputType::BECH32;
return true;
+ } else if (type == OUTPUT_TYPE_STRING_BECH32M) {
+ output_type = OutputType::BECH32M;
+ return true;
}
return false;
}
@@ -40,6 +44,7 @@ const std::string& FormatOutputType(OutputType type)
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
+ case OutputType::BECH32M: return OUTPUT_TYPE_STRING_BECH32M;
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -59,6 +64,7 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
return witdest;
}
}
+ case OutputType::BECH32M: {} // This function should never be used with BECH32M, so let it assert
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -98,6 +104,23 @@ CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore,
return ScriptHash(witprog);
}
}
+ case OutputType::BECH32M: {} // This function should not be used for BECH32M, so let it assert
} // no default case, so the compiler can warn about missing cases
assert(false);
}
+
+std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) {
+ if (std::holds_alternative<PKHash>(dest) ||
+ std::holds_alternative<ScriptHash>(dest)) {
+ return OutputType::LEGACY;
+ }
+ if (std::holds_alternative<WitnessV0KeyHash>(dest) ||
+ std::holds_alternative<WitnessV0ScriptHash>(dest)) {
+ return OutputType::BECH32;
+ }
+ if (std::holds_alternative<WitnessV1Taproot>(dest) ||
+ std::holds_alternative<WitnessUnknown>(dest)) {
+ return OutputType::BECH32M;
+ }
+ return std::nullopt;
+}
diff --git a/src/outputtype.h b/src/outputtype.h
index 88422e5824..2b83235cd0 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -18,12 +18,14 @@ enum class OutputType {
LEGACY,
P2SH_SEGWIT,
BECH32,
+ BECH32M,
};
static constexpr auto OUTPUT_TYPES = std::array{
OutputType::LEGACY,
OutputType::P2SH_SEGWIT,
OutputType::BECH32,
+ OutputType::BECH32M,
};
[[nodiscard]] bool ParseOutputType(const std::string& str, OutputType& output_type);
@@ -45,4 +47,7 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
*/
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType);
+/** Get the OutputType for a CTxDestination */
+std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest);
+
#endif // BITCOIN_OUTPUTTYPE_H
diff --git a/src/protocol.h b/src/protocol.h
index aaa9f1df40..f9248899dc 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -13,6 +13,7 @@
#include <netaddress.h>
#include <primitives/transaction.h>
#include <serialize.h>
+#include <streams.h>
#include <uint256.h>
#include <version.h>
@@ -358,6 +359,31 @@ class CAddress : public CService
{
static constexpr uint32_t TIME_INIT{100000000};
+ /** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with
+ * the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been
+ * disentangled from client versioning, and now instead:
+ * - The low bits (masked by DISK_VERSION_IGNORE_MASK) store the fixed value DISK_VERSION_INIT,
+ * (in case any code exists that treats it as a client version) but are ignored on
+ * deserialization.
+ * - The high bits (masked by ~DISK_VERSION_IGNORE_MASK) store actual serialization information.
+ * Only 0 or DISK_VERSION_ADDRV2 (equal to the historical ADDRV2_FORMAT) are valid now, and
+ * any other value triggers a deserialization failure. Other values can be added later if
+ * needed.
+ *
+ * For disk deserialization, ADDRV2_FORMAT in the stream version signals that ADDRV2
+ * deserialization is permitted, but the actual format is determined by the high bits in the
+ * stored version field. For network serialization, the stream version having ADDRV2_FORMAT or
+ * not determines the actual format used (as it has no embedded version number).
+ */
+ static constexpr uint32_t DISK_VERSION_INIT{220000};
+ static constexpr uint32_t DISK_VERSION_IGNORE_MASK{0b00000000'00000111'11111111'11111111};
+ /** The version number written in disk serialized addresses to indicate V2 serializations.
+ * It must be exactly 1<<29, as that is the value that historical versions used for this
+ * (they used their internal ADDRV2_FORMAT flag here). */
+ static constexpr uint32_t DISK_VERSION_ADDRV2{1 << 29};
+ static_assert((DISK_VERSION_INIT & ~DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_INIT must be covered by DISK_VERSION_IGNORE_MASK");
+ static_assert((DISK_VERSION_ADDRV2 & DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_ADDRV2 must not be covered by DISK_VERSION_IGNORE_MASK");
+
public:
CAddress() : CService{} {};
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
@@ -365,22 +391,48 @@ public:
SERIALIZE_METHODS(CAddress, obj)
{
- SER_READ(obj, obj.nTime = TIME_INIT);
- int nVersion = s.GetVersion();
+ // CAddress has a distinct network serialization and a disk serialization, but it should never
+ // be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's
+ // ambiguous what that would mean. Make sure no code relying on that is introduced:
+ assert(!(s.GetType() & SER_GETHASH));
+ bool use_v2;
+ bool store_time;
if (s.GetType() & SER_DISK) {
- READWRITE(nVersion);
- }
- if ((s.GetType() & SER_DISK) ||
- (nVersion != INIT_PROTO_VERSION && !(s.GetType() & SER_GETHASH))) {
+ // In the disk serialization format, the encoding (v1 or v2) is determined by a flag version
+ // that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines
+ // whether V2 is chosen/permitted at all.
+ uint32_t stored_format_version = DISK_VERSION_INIT;
+ if (s.GetVersion() & ADDRV2_FORMAT) stored_format_version |= DISK_VERSION_ADDRV2;
+ READWRITE(stored_format_version);
+ stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits
+ if (stored_format_version == 0) {
+ use_v2 = false;
+ } else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) {
+ // Only support v2 deserialization if ADDRV2_FORMAT is set.
+ use_v2 = true;
+ } else {
+ throw std::ios_base::failure("Unsupported CAddress disk format version");
+ }
+ store_time = true;
+ } else {
+ // In the network serialization format, the encoding (v1 or v2) is determined directly by
+ // the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version
+ // exists in the stream.
+ assert(s.GetType() & SER_NETWORK);
+ use_v2 = s.GetVersion() & ADDRV2_FORMAT;
// The only time we serialize a CAddress object without nTime is in
// the initial VERSION messages which contain two CAddress records.
// At that point, the serialization version is INIT_PROTO_VERSION.
// After the version handshake, serialization version is >=
// MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
// nTime.
- READWRITE(obj.nTime);
+ store_time = s.GetVersion() != INIT_PROTO_VERSION;
}
- if (nVersion & ADDRV2_FORMAT) {
+
+ SER_READ(obj, obj.nTime = TIME_INIT);
+ if (store_time) READWRITE(obj.nTime);
+ // nServices is serialized as CompactSize in V2; as uint64_t in V1.
+ if (use_v2) {
uint64_t services_tmp;
SER_WRITE(obj, services_tmp = obj.nServices);
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
@@ -388,13 +440,22 @@ public:
} else {
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
}
- READWRITEAS(CService, obj);
+ // Invoke V1/V2 serializer for CService parent object.
+ OverrideStream<Stream> os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0);
+ SerReadWriteMany(os, ser_action, ReadWriteAsHelper<CService>(obj));
}
- // disk and network only
+ //! Always included in serialization, except in the network format on INIT_PROTO_VERSION.
uint32_t nTime{TIME_INIT};
-
+ //! Serialized as uint64_t in V1, and as CompactSize in V2.
ServiceFlags nServices{NODE_NONE};
+
+ friend bool operator==(const CAddress& a, const CAddress& b)
+ {
+ return a.nTime == b.nTime &&
+ a.nServices == b.nServices &&
+ static_cast<const CService&>(a) == static_cast<const CService&>(b);
+ }
};
/** getdata message type flags */
diff --git a/src/psbt.cpp b/src/psbt.cpp
index a849b2ea53..5445bc8aa1 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -59,12 +59,15 @@ bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
{
- PSBTInput input = inputs[input_index];
+ const PSBTInput& input = inputs[input_index];
uint32_t prevout_index = tx->vin[input_index].prevout.n;
if (input.non_witness_utxo) {
if (prevout_index >= input.non_witness_utxo->vout.size()) {
return false;
}
+ if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
+ return false;
+ }
utxo = input.non_witness_utxo->vout[prevout_index];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
@@ -227,7 +230,24 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
psbt_out.FromSignatureData(sigdata);
}
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
+{
+ const CMutableTransaction& tx = *psbt.tx;
+ bool have_all_spent_outputs = true;
+ std::vector<CTxOut> utxos(tx.vin.size());
+ for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
+ if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
+ }
+ PrecomputedTransactionData txdata;
+ if (have_all_spent_outputs) {
+ txdata.Init(tx, std::move(utxos), true);
+ } else {
+ txdata.Init(tx, {}, true);
+ }
+ return txdata;
+}
+
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
{
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
@@ -267,10 +287,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
sigdata.witness = false;
bool sig_complete;
- if (use_dummy) {
+ if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} else {
- MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
}
// Verify that a witness signature was produced in case one was required.
@@ -302,8 +322,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
// PartiallySignedTransaction did not understand them), this will combine them into a final
// script.
bool complete = true;
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
}
return complete;
diff --git a/src/psbt.h b/src/psbt.h
index 96ae39fdb8..f6b82b43de 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -567,11 +567,18 @@ enum class PSBTRole {
std::string PSBTRoleName(PSBTRole role);
+/** Compute a PrecomputedTransactionData object from a psbt. */
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
+
/** Checks whether a PSBTInput is already signed. */
bool PSBTInputSigned(const PSBTInput& input);
-/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
+ *
+ * txdata should be the output of PrecomputePSBTData (which can be shared across
+ * multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
+ **/
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
/** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 51cc826b00..175a39b805 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -373,3 +373,7 @@ ECCVerifyHandle::~ECCVerifyHandle()
secp256k1_context_verify = nullptr;
}
}
+
+const secp256k1_context* GetVerifyContext() {
+ return secp256k1_context_verify;
+}
diff --git a/src/pubkey.h b/src/pubkey.h
index 152a48dd18..eec34a89c2 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -234,6 +234,10 @@ public:
* fail. */
bool IsFullyValid() const;
+ /** Test whether this is the 0 key (the result of default construction). This implies
+ * !IsFullyValid(). */
+ bool IsNull() const { return m_keydata.IsNull(); }
+
/** Construct an x-only pubkey from exactly 32 bytes. */
explicit XOnlyPubKey(Span<const unsigned char> bytes);
@@ -312,4 +316,10 @@ public:
~ECCVerifyHandle();
};
+typedef struct secp256k1_context_struct secp256k1_context;
+
+/** Access to the internal secp256k1 context used for verification. Only intended to be used
+ * by key.cpp. */
+const secp256k1_context* GetVerifyContext();
+
#endif // BITCOIN_PUBKEY_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 7024fc7654..c31f0aceea 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -114,12 +114,12 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
// Build context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
- contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
- contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
+ contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
+ contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
+ contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
if (tab == SendingTab) {
- contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
+ contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
}
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 9e6cf56d31..442c813a5a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -490,7 +490,8 @@ int GuiMain(int argc, char* argv[])
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- SetupServerArgs(node_context);
+ node_context.args = &gArgs;
+ SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 50f6e739e8..f8aeb01659 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -211,11 +211,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
-#ifdef ENABLE_WALLET
- if(enableWallet) {
- connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay);
- }
-#endif
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
@@ -690,7 +685,7 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
- connect(wallet_view, &WalletView::outOfSyncWarningClicked, walletFrame, &WalletFrame::outOfSyncWarningClicked);
+ connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
@@ -1178,14 +1173,12 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
void BitcoinGUI::changeEvent(QEvent *e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview")));
sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send")));
receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses")));
historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history")));
}
-#endif
QMainWindow::changeEvent(e);
@@ -1516,14 +1509,14 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name());
if (style != styleSheet()) {
setStyleSheet(style);
}
}
-#endif
+
+ QLabel::changeEvent(e);
}
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index bff253e58a..d2d4079ea9 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -42,6 +42,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Dumpfile version is not supported. This version of bitcoin-wallet "
"only supports version 1 dumpfiles. Got dumpfile with version %s"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and "
+"\"bech32\" address types"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -"
@@ -103,9 +106,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Total length of network version string (%i) exceeds maximum length (%i). "
"Reduce the number or size of uacomments."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Transaction needs a change address, but we can't generate it. Please call "
-"keypoolrefill first."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to replay blocks. You will need to rebuild the database using -"
"reindex-chainstate."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -160,6 +160,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got key that was not hex: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got value that was not hex: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Keypool ran out, please call keypoolrefill first"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Missing checksum"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: No %s addresses available."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to parse version %u as a uint32_t"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to write record to new wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
@@ -216,6 +217,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction has too long of a mempool chain"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction must have at least one recipient"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Transaction needs a change address, but we can't generate it. %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."),
@@ -228,6 +230,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown address type '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown change type '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unknown new rules activated (versionbit %i)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading txindex database"),
@@ -235,5 +238,4 @@ QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe chara
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Warning: unknown new rules activated (versionbit %i)"),
};
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 8ae0648141..d2a9365890 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -52,13 +52,13 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy address"), this, &CoinControlDialog::copyAddress);
- contextMenu->addAction(tr("Copy label"), this, &CoinControlDialog::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &CoinControlDialog::copyAmount);
- copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction ID"), this, &CoinControlDialog::copyTransactionHash);
+ contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
+ contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
+ copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
- lockAction = contextMenu->addAction(tr("Lock unspent"), this, &CoinControlDialog::lockCoin);
- unlockAction = contextMenu->addAction(tr("Unlock unspent"), this, &CoinControlDialog::unlockCoin);
+ lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
+ unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
// clipboard actions
@@ -564,11 +564,11 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
void CoinControlDialog::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
updateView();
}
-#endif
+
+ QDialog::changeEvent(e);
}
void CoinControlDialog::updateView()
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index e593697b46..dc24bbc6a6 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -31,8 +31,9 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
// Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
+#ifdef ENABLE_EXTERNAL_SIGNER
ui->external_signer_checkbox->setEnabled(!checked);
-
+#endif
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
ui->disable_privkeys_checkbox->setChecked(false);
@@ -112,8 +113,7 @@ CreateWalletDialog::~CreateWalletDialog()
delete ui;
}
-#ifdef ENABLE_EXTERNAL_SIGNER
-void CreateWalletDialog::setSigners(std::vector<ExternalSigner>& signers)
+void CreateWalletDialog::setSigners(const std::vector<ExternalSigner>& signers)
{
if (!signers.empty()) {
ui->external_signer_checkbox->setEnabled(true);
@@ -132,7 +132,6 @@ void CreateWalletDialog::setSigners(std::vector<ExternalSigner>& signers)
ui->external_signer_checkbox->setEnabled(false);
}
}
-#endif
QString CreateWalletDialog::walletName() const
{
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index 585b1461f7..25ddf97585 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -7,11 +7,8 @@
#include <QDialog>
-class WalletModel;
-
-#ifdef ENABLE_EXTERNAL_SIGNER
class ExternalSigner;
-#endif
+class WalletModel;
namespace Ui {
class CreateWalletDialog;
@@ -27,9 +24,7 @@ public:
explicit CreateWalletDialog(QWidget* parent);
virtual ~CreateWalletDialog();
-#ifdef ENABLE_EXTERNAL_SIGNER
- void setSigners(std::vector<ExternalSigner>& signers);
-#endif
+ void setSigners(const std::vector<ExternalSigner>& signers);
QString walletName() const;
bool isEncryptWalletChecked() const;
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 393dca8ccd..ecdfce2f5a 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -809,11 +809,10 @@ void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int
void ThemedLabel::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
updateThemedPixmap();
}
-#endif
+
QLabel::changeEvent(e);
}
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index b146489ba8..7026f49c01 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -55,11 +55,12 @@
</message>
<message>
<location line="-30"/>
+ <location filename="../addressbookpage.cpp" line="+122"/>
<source>&amp;Delete</source>
<translation>&amp;Delete</translation>
</message>
<message>
- <location filename="../addressbookpage.cpp" line="+84"/>
+ <location filename="../addressbookpage.cpp" line="-38"/>
<source>Choose the address to send coins to</source>
<translation type="unfinished"></translation>
</message>
@@ -96,26 +97,21 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+8"/>
- <source>Copy Address</source>
+ <source>&amp;Copy Address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy Label</source>
+ <source>Copy &amp;Label</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Edit</source>
+ <source>&amp;Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Delete</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+161"/>
+ <location line="+164"/>
<source>Export Address List</source>
<translation type="unfinished"></translation>
</message>
@@ -317,7 +313,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>BitcoinApplication</name>
<message>
- <location filename="../bitcoin.cpp" line="+420"/>
+ <location filename="../bitcoin.cpp" line="+421"/>
<source>Runaway exception</source>
<translation type="unfinished"></translation>
</message>
@@ -405,18 +401,18 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+358"/>
+ <location line="+373"/>
<source>Network activity disabled.</source>
<extracomment>A substring of the tooltip.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+426"/>
+ <location line="+424"/>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1096"/>
+ <location line="-1109"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -546,7 +542,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="+422"/>
+ <location line="+437"/>
<source>Syncing Headers (%1%)…</source>
<translation type="unfinished"></translation>
</message>
@@ -576,7 +572,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-766"/>
+ <location line="-781"/>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
<translation type="unfinished"></translation>
</message>
@@ -596,7 +592,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+678"/>
+ <location line="+693"/>
<source>Processed %n block(s) of transaction history.</source>
<translation>
<numerusform>Processed %n block of transaction history.</numerusform>
@@ -644,7 +640,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Up to date</translation>
</message>
<message>
- <location line="-715"/>
+ <location line="-730"/>
<source>Load Partially Signed Bitcoin Transaction</source>
<translation type="unfinished"></translation>
</message>
@@ -744,7 +740,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+248"/>
+ <location line="+263"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
@@ -792,7 +788,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+110"/>
+ <location line="+108"/>
<source>Date: %1
</source>
<translation type="unfinished"></translation>
@@ -956,34 +952,38 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+55"/>
- <source>Copy address</source>
+ <location filename="../coincontroldialog.cpp" line="+66"/>
+ <source>Copy amount</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-11"/>
+ <source>&amp;Copy address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy label</source>
+ <source>Copy &amp;label</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <location line="+9"/>
- <source>Copy amount</source>
+ <source>Copy &amp;amount</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-8"/>
- <source>Copy transaction ID</source>
+ <location line="+1"/>
+ <source>Copy transaction &amp;ID</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Lock unspent</source>
+ <source>L&amp;ock unspent</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Unlock unspent</source>
+ <source>&amp;Unlock unspent</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1061,12 +1061,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>CreateWalletActivity</name>
<message>
- <location filename="../walletcontroller.cpp" line="+253"/>
+ <location filename="../walletcontroller.cpp" line="+254"/>
<source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+31"/>
<source>Create wallet failed</source>
<translation type="unfinished"></translation>
</message>
@@ -1075,6 +1075,11 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<source>Create wallet warning</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+16"/>
+ <source>Can&apos;t list signers</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CreateWalletDialog</name>
@@ -1139,15 +1144,31 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../createwalletdialog.cpp" line="+21"/>
+ <location line="+7"/>
+ <source>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>External signer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../createwalletdialog.cpp" line="+22"/>
<source>Create</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+42"/>
+ <location line="+68"/>
<source>Compiled without sqlite support (required for descriptor wallets)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+14"/>
+ <source>Compiled without external signing support (required for external signing)</source>
+ <extracomment>&quot;External signing&quot; means using devices such as hardware wallets.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -1472,7 +1493,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>OpenWalletActivity</name>
<message>
- <location filename="../walletcontroller.cpp" line="+39"/>
+ <location filename="../walletcontroller.cpp" line="+32"/>
<source>Open wallet failed</source>
<translation type="unfinished"></translation>
</message>
@@ -1530,7 +1551,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+171"/>
+ <location line="+201"/>
<location line="+187"/>
<source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
<translation type="unfinished"></translation>
@@ -1579,7 +1600,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Network</translation>
</message>
<message>
- <location line="-188"/>
+ <location line="-218"/>
<source>Prune &amp;block storage to</source>
<translation type="unfinished"></translation>
</message>
@@ -1629,7 +1650,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+30"/>
+ <location line="+10"/>
+ <source>External Signer (e.g. hardware wallet)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>&amp;External signer script path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Full path to a Bitcoin Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+32"/>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
<translation>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</translation>
</message>
@@ -1762,12 +1798,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation>
</message>
<message>
- <location line="-463"/>
+ <location line="-493"/>
<source>Whether to show coin control features or not.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+260"/>
+ <location line="+290"/>
<source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source>
<translation type="unfinished"></translation>
</message>
@@ -1812,7 +1848,13 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Cancel</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+104"/>
+ <location filename="../optionsdialog.cpp" line="+97"/>
+ <source>Compiled without external signing support (required for external signing)</source>
+ <extracomment>&quot;External signing&quot; means using devices such as hardware wallets.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
<source>default</source>
<translation>default</translation>
</message>
@@ -1822,7 +1864,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+91"/>
+ <location line="+93"/>
<source>Confirm options reset</source>
<translation>Confirm options reset</translation>
</message>
@@ -1962,7 +2004,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../overviewpage.cpp" line="+193"/>
+ <location filename="../overviewpage.cpp" line="+188"/>
<source>Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings-&gt;Mask values.</source>
<translation type="unfinished"></translation>
</message>
@@ -2168,7 +2210,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<context>
<name>PeerTableModel</name>
<message>
- <location filename="../peertablemodel.h" line="+101"/>
+ <location filename="../peertablemodel.h" line="+107"/>
<source>User Agent</source>
<extracomment>Title of Peers Table column which contains the peer&apos;s User Agent string.</extracomment>
<translation type="unfinished"></translation>
@@ -2384,7 +2426,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+111"/>
+ <location filename="../bitcoin.cpp" line="+112"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
@@ -2418,12 +2460,12 @@ If you are receiving this error you should request the merchant provide a BIP21
<name>QRImageWidget</name>
<message>
<location filename="../qrimagewidget.cpp" line="+30"/>
- <source>Save Image…</source>
+ <source>&amp;Save Image…</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy Image</source>
+ <source>&amp;Copy Image</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2467,7 +2509,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+722"/>
+ <location line="+692"/>
<location line="+26"/>
<location line="+26"/>
<location line="+23"/>
@@ -2490,12 +2532,12 @@ If you are receiving this error you should request the merchant provide a BIP21
<location line="+23"/>
<location line="+23"/>
<location line="+26"/>
- <location filename="../rpcconsole.h" line="+138"/>
+ <location filename="../rpcconsole.h" line="+139"/>
<source>N/A</source>
<translation>N/A</translation>
</message>
<message>
- <location line="-1549"/>
+ <location line="-1519"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2536,12 +2578,12 @@ If you are receiving this error you should request the merchant provide a BIP21
</message>
<message>
<location line="+29"/>
- <location line="+922"/>
+ <location line="+892"/>
<source>Network</source>
<translation>Network</translation>
</message>
<message>
- <location line="-915"/>
+ <location line="-885"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
@@ -2581,7 +2623,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+241"/>
+ <location line="+211"/>
<source>&amp;Reset</source>
<translation type="unfinished"></translation>
</message>
@@ -2609,7 +2651,7 @@ If you are receiving this error you should request the merchant provide a BIP21
</message>
<message>
<location line="+68"/>
- <location filename="../rpcconsole.cpp" line="+1091"/>
+ <location filename="../rpcconsole.cpp" line="+1124"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
@@ -2644,13 +2686,13 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1516"/>
- <location line="+1081"/>
+ <location line="-1486"/>
+ <location line="+1051"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1155"/>
+ <location line="-1125"/>
<source>Node window</source>
<translation type="unfinished"></translation>
</message>
@@ -2665,17 +2707,17 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+84"/>
+ <location line="+78"/>
<source>Decrease font size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+32"/>
+ <location line="+20"/>
<source>Increase font size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+558"/>
+ <location line="+546"/>
<source>Permissions</source>
<translation type="unfinished"></translation>
</message>
@@ -2780,7 +2822,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1288"/>
+ <location line="-1258"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2795,7 +2837,7 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation>&amp;Console</translation>
</message>
<message>
- <location line="+217"/>
+ <location line="+187"/>
<source>&amp;Network Traffic</source>
<translation type="unfinished"></translation>
</message>
@@ -2815,12 +2857,12 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../forms/debugwindow.ui" line="-321"/>
+ <location filename="../forms/debugwindow.ui" line="-291"/>
<source>Debug log file</source>
<translation>Debug log file</translation>
</message>
<message>
- <location line="+155"/>
+ <location line="+125"/>
<source>Clear console</source>
<translation>Clear console</translation>
</message>
@@ -2850,12 +2892,12 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+40"/>
<source>Never</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-417"/>
+ <location filename="../rpcconsole.cpp" line="-429"/>
<source>Inbound: initiated by peer</source>
<translation type="unfinished"></translation>
</message>
@@ -2924,52 +2966,52 @@ If you are receiving this error you should request the merchant provide a BIP21
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+385"/>
- <source>Network activity disabled</source>
+ <location line="+150"/>
+ <source>&amp;Disconnect</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+77"/>
- <source>Executing command without any wallet</source>
+ <location line="+1"/>
+ <source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-2"/>
- <source>Executing command using &quot;%1&quot; wallet</source>
+ <location line="+1"/>
+ <source>1 d&amp;ay</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-319"/>
- <source>Disconnect</source>
+ <location line="+1"/>
+ <source>1 &amp;week</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>1 hour</source>
+ <source>1 &amp;year</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>1 day</source>
+ <location line="+22"/>
+ <source>&amp;Unban</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>1 week</source>
+ <location line="+221"/>
+ <source>Network activity disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>1 year</source>
+ <location line="+77"/>
+ <source>Executing command without any wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
- <source>Unban</source>
+ <location line="-2"/>
+ <source>Executing command using &quot;%1&quot; wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+150"/>
+ <location line="-146"/>
<source>Welcome to the %1 RPC console.
Use up and down arrows to navigate history, and %2 to clear screen.
Use %3 and %4 to increase or decrease the font size.
@@ -2997,7 +3039,7 @@ For more information on using this console, type %6.
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.h" line="-38"/>
+ <location filename="../rpcconsole.h" line="-40"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
@@ -3102,27 +3144,27 @@ For more information on using this console, type %6.
</message>
<message>
<location filename="../receivecoinsdialog.cpp" line="+47"/>
- <source>Copy URI</source>
+ <source>Copy &amp;URI</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy address</source>
+ <source>&amp;Copy address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy label</source>
+ <source>Copy &amp;label</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy message</source>
+ <source>Copy &amp;message</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy amount</source>
+ <source>Copy &amp;amount</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -3180,6 +3222,16 @@ For more information on using this console, type %6.
</message>
<message>
<location line="+10"/>
+ <source>&amp;Verify</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Verify this address on e.g. a hardware wallet screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
<source>&amp;Save Image…</source>
<translation type="unfinished"></translation>
</message>
@@ -3189,7 +3241,7 @@ For more information on using this console, type %6.
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../forms/receiverequestdialog.ui" line="-221"/>
+ <location filename="../forms/receiverequestdialog.ui" line="-234"/>
<source>Payment information</source>
<translation type="unfinished"></translation>
</message>
@@ -3197,7 +3249,7 @@ For more information on using this console, type %6.
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+30"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+32"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3236,7 +3288,7 @@ For more information on using this console, type %6.
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+674"/>
+ <location filename="../sendcoinsdialog.cpp" line="+738"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -3423,7 +3475,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-582"/>
+ <location filename="../sendcoinsdialog.cpp" line="-646"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -3463,7 +3515,24 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+29"/>
+ <location line="+30"/>
+ <source>Sign on device</source>
+ <extracomment>&quot;device&quot; usually means a hardware wallet</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Connect your hardware wallet first.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Set external signer script path in Options -&gt; Wallet</source>
+ <extracomment>&quot;External signer&quot; means using devices such as hardware wallets.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Cr&amp;eate Unsigned</source>
<translation type="unfinished"></translation>
</message>
@@ -3508,7 +3577,29 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+44"/>
+ <location line="+0"/>
+ <source>Sign and send</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+25"/>
+ <source>Sign failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>External signer not found</source>
+ <extracomment>&quot;External signer&quot; means using devices such as hardware wallets.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>External signer failure</source>
+ <extracomment>&quot;External signer&quot; means using devices such as hardware wallets.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+58"/>
<source>Save Transaction Data</source>
<translation type="unfinished"></translation>
</message>
@@ -3524,7 +3615,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-76"/>
+ <location line="+175"/>
+ <source>External balance:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-302"/>
<source>or</source>
<translation type="unfinished"></translation>
</message>
@@ -3569,12 +3665,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Send</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+229"/>
+ <location line="+283"/>
<source>Watch-only balance:</source>
<translation type="unfinished"></translation>
</message>
@@ -4434,62 +4525,62 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
- <source>Abandon transaction</source>
+ <location line="-26"/>
+ <source>Range…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-3"/>
- <source>Increase transaction fee</source>
+ <location line="+90"/>
+ <source>&amp;Copy address</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-8"/>
- <source>Copy address</source>
+ <location line="+1"/>
+ <source>Copy &amp;label</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy label</source>
+ <source>Copy &amp;amount</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy amount</source>
+ <source>Copy transaction &amp;ID</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy transaction ID</source>
+ <source>Copy &amp;raw transaction</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy raw transaction</source>
+ <source>Copy full transaction &amp;details</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy full transaction details</source>
+ <source>&amp;Show transaction details</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Edit address label</source>
+ <location line="+2"/>
+ <source>Increase transaction &amp;fee</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-6"/>
- <source>Show transaction details</source>
+ <location line="+3"/>
+ <source>A&amp;bandon transaction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-96"/>
- <source>Range…</source>
+ <location line="+1"/>
+ <source>&amp;Edit address label</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+276"/>
+ <location line="+174"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
@@ -4576,7 +4667,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<context>
<name>WalletController</name>
<message>
- <location filename="../walletcontroller.cpp" line="-250"/>
+ <location filename="../walletcontroller.cpp" line="-262"/>
<source>Close wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4604,7 +4695,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<context>
<name>WalletFrame</name>
<message>
- <location filename="../walletframe.cpp" line="+39"/>
+ <location filename="../walletframe.cpp" line="+35"/>
<source>No wallet has been loaded.
Go to File &gt; Open Wallet to load a wallet.
- OR -</source>
@@ -4624,7 +4715,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+279"/>
+ <location line="+260"/>
<location line="+52"/>
<location line="+13"/>
<location line="+5"/>
@@ -4692,7 +4783,12 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+14"/>
+ <source>Can&apos;t display address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+18"/>
<source>default wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4842,6 +4938,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+3"/>
+ <source>Error: Legacy wallets only support the &quot;legacy&quot;, &quot;p2sh-segwit&quot;, and &quot;bech32&quot; address types</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
@@ -4947,11 +5048,6 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+3"/>
- <source>Transaction needs a change address, but we can&apos;t generate it. Please call keypoolrefill first.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<translation type="unfinished"></translation>
</message>
@@ -5162,6 +5258,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Error: No %s addresses available.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error: Unable to parse version %u as a uint32_t</source>
<translation type="unfinished"></translation>
</message>
@@ -5442,6 +5543,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Transaction needs a change address, but we can&apos;t generate it. %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Transaction too large</source>
<translation type="unfinished"></translation>
</message>
@@ -5502,6 +5608,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Unknown new rules activated (versionbit %i)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
@@ -5535,10 +5646,5 @@ Go to File &gt; Open Wallet to load a wallet.
<source>Wallet needed to be rewritten: restart %s to complete</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <location line="+1"/>
- <source>Warning: unknown new rules activated (versionbit %i)</source>
- <translation type="unfinished"></translation>
- </message>
</context>
</TS>
diff --git a/src/qt/locale/bitcoin_en.xlf b/src/qt/locale/bitcoin_en.xlf
index ffe7812738..caefddc663 100644
--- a/src/qt/locale/bitcoin_en.xlf
+++ b/src/qt/locale/bitcoin_en.xlf
@@ -56,6 +56,7 @@
<source xml:space="preserve">&amp;Delete</source>
<target xml:space="preserve">&amp;Delete</target>
<context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../addressbookpage.cpp</context><context context-type="linenumber">122</context></context-group>
</trans-unit>
</group>
</body></file>
@@ -98,43 +99,38 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context-group purpose="location"><context context-type="linenumber">109</context></context-group>
</trans-unit>
<trans-unit id="_msg19">
- <source xml:space="preserve">Copy Address</source>
+ <source xml:space="preserve">&amp;Copy Address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">117</context></context-group>
</trans-unit>
<trans-unit id="_msg20">
- <source xml:space="preserve">Copy Label</source>
+ <source xml:space="preserve">Copy &amp;Label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">118</context></context-group>
</trans-unit>
<trans-unit id="_msg21">
- <source xml:space="preserve">Edit</source>
+ <source xml:space="preserve">&amp;Edit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">119</context></context-group>
</trans-unit>
<trans-unit id="_msg22">
- <source xml:space="preserve">Delete</source>
- <target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
- </trans-unit>
- <trans-unit id="_msg23">
<source xml:space="preserve">Export Address List</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">283</context></context-group>
</trans-unit>
- <trans-unit id="_msg24">
+ <trans-unit id="_msg23">
<source xml:space="preserve">Comma separated file</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">286</context></context-group>
<note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note>
</trans-unit>
- <trans-unit id="_msg25">
+ <trans-unit id="_msg24">
<source xml:space="preserve">There was an error trying to save the address list to %1. Please try again.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">302</context></context-group>
<note annotates="source" from="developer">An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</note>
</trans-unit>
- <trans-unit id="_msg26">
+ <trans-unit id="_msg25">
<source xml:space="preserve">Exporting Failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">299</context></context-group>
@@ -143,17 +139,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../addresstablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="AddressTableModel">
- <trans-unit id="_msg27">
+ <trans-unit id="_msg26">
<source xml:space="preserve">Label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">168</context></context-group>
</trans-unit>
- <trans-unit id="_msg28">
+ <trans-unit id="_msg27">
<source xml:space="preserve">Address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">168</context></context-group>
</trans-unit>
- <trans-unit id="_msg29">
+ <trans-unit id="_msg28">
<source xml:space="preserve">(no label)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">206</context></context-group>
@@ -162,27 +158,27 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../forms/askpassphrasedialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="AskPassphraseDialog">
- <trans-unit id="_msg30" approved="yes">
+ <trans-unit id="_msg29" approved="yes">
<source xml:space="preserve">Passphrase Dialog</source>
<target xml:space="preserve">Passphrase Dialog</target>
<context-group purpose="location"><context context-type="linenumber">26</context></context-group>
</trans-unit>
- <trans-unit id="_msg31" approved="yes">
+ <trans-unit id="_msg30" approved="yes">
<source xml:space="preserve">Enter passphrase</source>
<target xml:space="preserve">Enter passphrase</target>
<context-group purpose="location"><context context-type="linenumber">56</context></context-group>
</trans-unit>
- <trans-unit id="_msg32" approved="yes">
+ <trans-unit id="_msg31" approved="yes">
<source xml:space="preserve">New passphrase</source>
<target xml:space="preserve">New passphrase</target>
<context-group purpose="location"><context context-type="linenumber">70</context></context-group>
</trans-unit>
- <trans-unit id="_msg33" approved="yes">
+ <trans-unit id="_msg32" approved="yes">
<source xml:space="preserve">Repeat new passphrase</source>
<target xml:space="preserve">Repeat new passphrase</target>
<context-group purpose="location"><context context-type="linenumber">84</context></context-group>
</trans-unit>
- <trans-unit id="_msg34">
+ <trans-unit id="_msg33">
<source xml:space="preserve">Show passphrase</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">98</context></context-group>
@@ -191,83 +187,83 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../askpassphrasedialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="AskPassphraseDialog">
- <trans-unit id="_msg35">
+ <trans-unit id="_msg34">
<source xml:space="preserve">Encrypt wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">51</context></context-group>
</trans-unit>
- <trans-unit id="_msg36">
+ <trans-unit id="_msg35">
<source xml:space="preserve">This operation needs your wallet passphrase to unlock the wallet.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">54</context></context-group>
</trans-unit>
- <trans-unit id="_msg37">
+ <trans-unit id="_msg36">
<source xml:space="preserve">Unlock wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">59</context></context-group>
</trans-unit>
- <trans-unit id="_msg38">
+ <trans-unit id="_msg37">
<source xml:space="preserve">Change passphrase</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">62</context></context-group>
</trans-unit>
- <trans-unit id="_msg39">
+ <trans-unit id="_msg38">
<source xml:space="preserve">Confirm wallet encryption</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">110</context></context-group>
</trans-unit>
- <trans-unit id="_msg40">
+ <trans-unit id="_msg39">
<source xml:space="preserve">Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
- <trans-unit id="_msg41">
+ <trans-unit id="_msg40">
<source xml:space="preserve">Are you sure you wish to encrypt your wallet?</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
- <trans-unit id="_msg42">
+ <trans-unit id="_msg41">
<source xml:space="preserve">Wallet encrypted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">129</context></context-group>
<context-group purpose="location"><context context-type="linenumber">173</context></context-group>
</trans-unit>
- <trans-unit id="_msg43">
+ <trans-unit id="_msg42">
<source xml:space="preserve">Enter the new passphrase for the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">48</context></context-group>
</trans-unit>
- <trans-unit id="_msg44">
+ <trans-unit id="_msg43">
<source xml:space="preserve">Enter the old passphrase and new passphrase for the wallet.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">63</context></context-group>
</trans-unit>
- <trans-unit id="_msg45">
+ <trans-unit id="_msg44">
<source xml:space="preserve">Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">118</context></context-group>
</trans-unit>
- <trans-unit id="_msg46">
+ <trans-unit id="_msg45">
<source xml:space="preserve">Wallet to be encrypted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">122</context></context-group>
</trans-unit>
- <trans-unit id="_msg47">
+ <trans-unit id="_msg46">
<source xml:space="preserve">Your wallet is about to be encrypted. </source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">124</context></context-group>
</trans-unit>
- <trans-unit id="_msg48">
+ <trans-unit id="_msg47">
<source xml:space="preserve">Your wallet is now encrypted. </source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">131</context></context-group>
</trans-unit>
- <trans-unit id="_msg49">
+ <trans-unit id="_msg48">
<source xml:space="preserve">IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">133</context></context-group>
</trans-unit>
- <trans-unit id="_msg50">
+ <trans-unit id="_msg49">
<source xml:space="preserve">Wallet encryption failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">139</context></context-group>
@@ -275,35 +271,35 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context-group purpose="location"><context context-type="linenumber">179</context></context-group>
<context-group purpose="location"><context context-type="linenumber">185</context></context-group>
</trans-unit>
- <trans-unit id="_msg51">
+ <trans-unit id="_msg50">
<source xml:space="preserve">Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">140</context></context-group>
</trans-unit>
- <trans-unit id="_msg52">
+ <trans-unit id="_msg51">
<source xml:space="preserve">The supplied passphrases do not match.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">148</context></context-group>
<context-group purpose="location"><context context-type="linenumber">186</context></context-group>
</trans-unit>
- <trans-unit id="_msg53">
+ <trans-unit id="_msg52">
<source xml:space="preserve">Wallet unlock failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">159</context></context-group>
<context-group purpose="location"><context context-type="linenumber">165</context></context-group>
</trans-unit>
- <trans-unit id="_msg54">
+ <trans-unit id="_msg53">
<source xml:space="preserve">The passphrase entered for the wallet decryption was incorrect.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">160</context></context-group>
<context-group purpose="location"><context context-type="linenumber">180</context></context-group>
</trans-unit>
- <trans-unit id="_msg55">
+ <trans-unit id="_msg54">
<source xml:space="preserve">Wallet passphrase was successfully changed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">174</context></context-group>
</trans-unit>
- <trans-unit id="_msg56">
+ <trans-unit id="_msg55">
<source xml:space="preserve">Warning: The Caps Lock key is on!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">220</context></context-group>
@@ -313,12 +309,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../bantablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="BanTableModel">
- <trans-unit id="_msg57">
+ <trans-unit id="_msg56">
<source xml:space="preserve">IP/Netmask</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">85</context></context-group>
</trans-unit>
- <trans-unit id="_msg58">
+ <trans-unit id="_msg57">
<source xml:space="preserve">Banned Until</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">85</context></context-group>
@@ -327,683 +323,683 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../bitcoin.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="BitcoinApplication">
- <trans-unit id="_msg59">
+ <trans-unit id="_msg58">
<source xml:space="preserve">Runaway exception</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">420</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">421</context></context-group>
</trans-unit>
- <trans-unit id="_msg60">
+ <trans-unit id="_msg59">
<source xml:space="preserve">A fatal error occurred. %1 can no longer continue safely and will quit.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">421</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">422</context></context-group>
</trans-unit>
- <trans-unit id="_msg61">
+ <trans-unit id="_msg60">
<source xml:space="preserve">Internal error</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">430</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">431</context></context-group>
</trans-unit>
- <trans-unit id="_msg62">
+ <trans-unit id="_msg61">
<source xml:space="preserve">An internal error occurred. %1 will attempt to continue safely. This is an unexpected bug which can be reported as described below.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">431</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">432</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="QObject">
- <trans-unit id="_msg63">
+ <trans-unit id="_msg62">
<source xml:space="preserve">Error: Specified data directory &quot;%1&quot; does not exist.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">542</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">544</context></context-group>
</trans-unit>
- <trans-unit id="_msg64">
+ <trans-unit id="_msg63">
<source xml:space="preserve">Error: Cannot parse configuration file: %1.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">548</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">550</context></context-group>
</trans-unit>
- <trans-unit id="_msg65">
+ <trans-unit id="_msg64">
<source xml:space="preserve">Error: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">563</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">565</context></context-group>
</trans-unit>
- <trans-unit id="_msg66">
+ <trans-unit id="_msg65">
<source xml:space="preserve">Error initializing settings: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">572</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">574</context></context-group>
</trans-unit>
- <trans-unit id="_msg67">
+ <trans-unit id="_msg66">
<source xml:space="preserve">%1 didn&apos;t yet exit safely…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">635</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">637</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../bitcoingui.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="BitcoinGUI">
- <trans-unit id="_msg68" approved="yes">
+ <trans-unit id="_msg67" approved="yes">
<source xml:space="preserve">&amp;Overview</source>
<target xml:space="preserve">&amp;Overview</target>
<context-group purpose="location"><context context-type="linenumber">245</context></context-group>
</trans-unit>
- <trans-unit id="_msg69" approved="yes">
+ <trans-unit id="_msg68" approved="yes">
<source xml:space="preserve">Show general overview of wallet</source>
<target xml:space="preserve">Show general overview of wallet</target>
<context-group purpose="location"><context context-type="linenumber">246</context></context-group>
</trans-unit>
- <trans-unit id="_msg70" approved="yes">
+ <trans-unit id="_msg69" approved="yes">
<source xml:space="preserve">&amp;Transactions</source>
<target xml:space="preserve">&amp;Transactions</target>
<context-group purpose="location"><context context-type="linenumber">274</context></context-group>
</trans-unit>
- <trans-unit id="_msg71" approved="yes">
+ <trans-unit id="_msg70" approved="yes">
<source xml:space="preserve">Browse transaction history</source>
<target xml:space="preserve">Browse transaction history</target>
<context-group purpose="location"><context context-type="linenumber">275</context></context-group>
</trans-unit>
- <trans-unit id="_msg72" approved="yes">
+ <trans-unit id="_msg71" approved="yes">
<source xml:space="preserve">E&amp;xit</source>
<target xml:space="preserve">E&amp;xit</target>
<context-group purpose="location"><context context-type="linenumber">298</context></context-group>
</trans-unit>
- <trans-unit id="_msg73" approved="yes">
+ <trans-unit id="_msg72" approved="yes">
<source xml:space="preserve">Quit application</source>
<target xml:space="preserve">Quit application</target>
<context-group purpose="location"><context context-type="linenumber">299</context></context-group>
</trans-unit>
- <trans-unit id="_msg74">
+ <trans-unit id="_msg73">
<source xml:space="preserve">&amp;About %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">302</context></context-group>
</trans-unit>
- <trans-unit id="_msg75">
+ <trans-unit id="_msg74">
<source xml:space="preserve">Show information about %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">303</context></context-group>
</trans-unit>
- <trans-unit id="_msg76" approved="yes">
+ <trans-unit id="_msg75" approved="yes">
<source xml:space="preserve">About &amp;Qt</source>
<target xml:space="preserve">About &amp;Qt</target>
<context-group purpose="location"><context context-type="linenumber">306</context></context-group>
</trans-unit>
- <trans-unit id="_msg77" approved="yes">
+ <trans-unit id="_msg76" approved="yes">
<source xml:space="preserve">Show information about Qt</source>
<target xml:space="preserve">Show information about Qt</target>
<context-group purpose="location"><context context-type="linenumber">307</context></context-group>
</trans-unit>
- <trans-unit id="_msg78">
+ <trans-unit id="_msg77">
<source xml:space="preserve">Modify configuration options for %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">310</context></context-group>
</trans-unit>
- <trans-unit id="_msg79">
+ <trans-unit id="_msg78">
<source xml:space="preserve">Create a new wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">356</context></context-group>
</trans-unit>
- <trans-unit id="_msg80">
+ <trans-unit id="_msg79">
<source xml:space="preserve">Wallet:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">565</context></context-group>
</trans-unit>
- <trans-unit id="_msg81">
+ <trans-unit id="_msg80">
<source xml:space="preserve">Network activity disabled.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">923</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">938</context></context-group>
<note annotates="source" from="developer">A substring of the tooltip.</note>
</trans-unit>
- <trans-unit id="_msg82">
+ <trans-unit id="_msg81">
<source xml:space="preserve">Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1349</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1362</context></context-group>
</trans-unit>
- <trans-unit id="_msg83" approved="yes">
+ <trans-unit id="_msg82" approved="yes">
<source xml:space="preserve">Send coins to a Bitcoin address</source>
<target xml:space="preserve">Send coins to a Bitcoin address</target>
<context-group purpose="location"><context context-type="linenumber">253</context></context-group>
</trans-unit>
- <trans-unit id="_msg84" approved="yes">
+ <trans-unit id="_msg83" approved="yes">
<source xml:space="preserve">Backup wallet to another location</source>
<target xml:space="preserve">Backup wallet to another location</target>
<context-group purpose="location"><context context-type="linenumber">320</context></context-group>
</trans-unit>
- <trans-unit id="_msg85" approved="yes">
+ <trans-unit id="_msg84" approved="yes">
<source xml:space="preserve">Change the passphrase used for wallet encryption</source>
<target xml:space="preserve">Change the passphrase used for wallet encryption</target>
<context-group purpose="location"><context context-type="linenumber">322</context></context-group>
</trans-unit>
- <trans-unit id="_msg86" approved="yes">
+ <trans-unit id="_msg85" approved="yes">
<source xml:space="preserve">&amp;Send</source>
<target xml:space="preserve">&amp;Send</target>
<context-group purpose="location"><context context-type="linenumber">252</context></context-group>
</trans-unit>
- <trans-unit id="_msg87" approved="yes">
+ <trans-unit id="_msg86" approved="yes">
<source xml:space="preserve">&amp;Receive</source>
<target xml:space="preserve">&amp;Receive</target>
<context-group purpose="location"><context context-type="linenumber">263</context></context-group>
</trans-unit>
- <trans-unit id="_msg88">
+ <trans-unit id="_msg87">
<source xml:space="preserve">&amp;Options…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">309</context></context-group>
</trans-unit>
- <trans-unit id="_msg89" approved="yes">
+ <trans-unit id="_msg88" approved="yes">
<source xml:space="preserve">&amp;Show / Hide</source>
<target xml:space="preserve">&amp;Show / Hide</target>
<context-group purpose="location"><context context-type="linenumber">313</context></context-group>
</trans-unit>
- <trans-unit id="_msg90" approved="yes">
+ <trans-unit id="_msg89" approved="yes">
<source xml:space="preserve">Show or hide the main Window</source>
<target xml:space="preserve">Show or hide the main Window</target>
<context-group purpose="location"><context context-type="linenumber">314</context></context-group>
</trans-unit>
- <trans-unit id="_msg91">
+ <trans-unit id="_msg90">
<source xml:space="preserve">&amp;Encrypt Wallet…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">316</context></context-group>
</trans-unit>
- <trans-unit id="_msg92" approved="yes">
+ <trans-unit id="_msg91" approved="yes">
<source xml:space="preserve">Encrypt the private keys that belong to your wallet</source>
<target xml:space="preserve">Encrypt the private keys that belong to your wallet</target>
<context-group purpose="location"><context context-type="linenumber">317</context></context-group>
</trans-unit>
- <trans-unit id="_msg93">
+ <trans-unit id="_msg92">
<source xml:space="preserve">&amp;Backup Wallet…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">319</context></context-group>
</trans-unit>
- <trans-unit id="_msg94">
+ <trans-unit id="_msg93">
<source xml:space="preserve">&amp;Change Passphrase…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">321</context></context-group>
</trans-unit>
- <trans-unit id="_msg95">
+ <trans-unit id="_msg94">
<source xml:space="preserve">Sign &amp;message…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">323</context></context-group>
</trans-unit>
- <trans-unit id="_msg96" approved="yes">
+ <trans-unit id="_msg95" approved="yes">
<source xml:space="preserve">Sign messages with your Bitcoin addresses to prove you own them</source>
<target xml:space="preserve">Sign messages with your Bitcoin addresses to prove you own them</target>
<context-group purpose="location"><context context-type="linenumber">324</context></context-group>
</trans-unit>
- <trans-unit id="_msg97">
+ <trans-unit id="_msg96">
<source xml:space="preserve">&amp;Verify message…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">325</context></context-group>
</trans-unit>
- <trans-unit id="_msg98" approved="yes">
+ <trans-unit id="_msg97" approved="yes">
<source xml:space="preserve">Verify messages to ensure they were signed with specified Bitcoin addresses</source>
<target xml:space="preserve">Verify messages to ensure they were signed with specified Bitcoin addresses</target>
<context-group purpose="location"><context context-type="linenumber">326</context></context-group>
</trans-unit>
- <trans-unit id="_msg99">
+ <trans-unit id="_msg98">
<source xml:space="preserve">&amp;Load PSBT from file…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">327</context></context-group>
</trans-unit>
- <trans-unit id="_msg100">
+ <trans-unit id="_msg99">
<source xml:space="preserve">Load PSBT from clipboard…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">329</context></context-group>
</trans-unit>
- <trans-unit id="_msg101">
+ <trans-unit id="_msg100">
<source xml:space="preserve">Open &amp;URI…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">343</context></context-group>
</trans-unit>
- <trans-unit id="_msg102">
+ <trans-unit id="_msg101">
<source xml:space="preserve">Close Wallet…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">351</context></context-group>
</trans-unit>
- <trans-unit id="_msg103">
+ <trans-unit id="_msg102">
<source xml:space="preserve">Create Wallet…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">354</context></context-group>
</trans-unit>
- <trans-unit id="_msg104">
+ <trans-unit id="_msg103">
<source xml:space="preserve">Close All Wallets…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">358</context></context-group>
</trans-unit>
- <trans-unit id="_msg105" approved="yes">
+ <trans-unit id="_msg104" approved="yes">
<source xml:space="preserve">&amp;File</source>
<target xml:space="preserve">&amp;File</target>
<context-group purpose="location"><context context-type="linenumber">455</context></context-group>
</trans-unit>
- <trans-unit id="_msg106" approved="yes">
+ <trans-unit id="_msg105" approved="yes">
<source xml:space="preserve">&amp;Settings</source>
<target xml:space="preserve">&amp;Settings</target>
<context-group purpose="location"><context context-type="linenumber">473</context></context-group>
</trans-unit>
- <trans-unit id="_msg107" approved="yes">
+ <trans-unit id="_msg106" approved="yes">
<source xml:space="preserve">&amp;Help</source>
<target xml:space="preserve">&amp;Help</target>
<context-group purpose="location"><context context-type="linenumber">534</context></context-group>
</trans-unit>
- <trans-unit id="_msg108" approved="yes">
+ <trans-unit id="_msg107" approved="yes">
<source xml:space="preserve">Tabs toolbar</source>
<target xml:space="preserve">Tabs toolbar</target>
<context-group purpose="location"><context context-type="linenumber">545</context></context-group>
</trans-unit>
- <trans-unit id="_msg109">
+ <trans-unit id="_msg108">
<source xml:space="preserve">Syncing Headers (%1%)…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">967</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">982</context></context-group>
</trans-unit>
- <trans-unit id="_msg110">
+ <trans-unit id="_msg109">
<source xml:space="preserve">Synchronizing with network…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1013</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1028</context></context-group>
</trans-unit>
- <trans-unit id="_msg111">
+ <trans-unit id="_msg110">
<source xml:space="preserve">Indexing blocks on disk…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1018</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1033</context></context-group>
</trans-unit>
- <trans-unit id="_msg112">
+ <trans-unit id="_msg111">
<source xml:space="preserve">Processing blocks on disk…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1020</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1035</context></context-group>
</trans-unit>
- <trans-unit id="_msg113">
+ <trans-unit id="_msg112">
<source xml:space="preserve">Reindexing blocks on disk…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1024</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1039</context></context-group>
</trans-unit>
- <trans-unit id="_msg114">
+ <trans-unit id="_msg113">
<source xml:space="preserve">Connecting to peers…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1030</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1045</context></context-group>
</trans-unit>
- <trans-unit id="_msg115">
+ <trans-unit id="_msg114">
<source xml:space="preserve">Request payments (generates QR codes and bitcoin: URIs)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">264</context></context-group>
</trans-unit>
- <trans-unit id="_msg116">
+ <trans-unit id="_msg115">
<source xml:space="preserve">Show the list of used sending addresses and labels</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">339</context></context-group>
</trans-unit>
- <trans-unit id="_msg117">
+ <trans-unit id="_msg116">
<source xml:space="preserve">Show the list of used receiving addresses and labels</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">341</context></context-group>
</trans-unit>
- <trans-unit id="_msg118">
+ <trans-unit id="_msg117">
<source xml:space="preserve">&amp;Command-line options</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">361</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
- <context-group purpose="location"><context context-type="linenumber">1039</context></context-group>
- <trans-unit id="_msg119[0]" approved="yes">
+ <context-group purpose="location"><context context-type="linenumber">1054</context></context-group>
+ <trans-unit id="_msg118[0]" approved="yes">
<source xml:space="preserve">Processed %n block(s) of transaction history.</source>
<target xml:space="preserve">Processed %n block of transaction history.</target>
</trans-unit>
- <trans-unit id="_msg119[1]" approved="yes">
+ <trans-unit id="_msg118[1]" approved="yes">
<source xml:space="preserve">Processed %n block(s) of transaction history.</source>
<target xml:space="preserve">Processed %n blocks of transaction history.</target>
</trans-unit>
</group>
- <trans-unit id="_msg120" approved="yes">
+ <trans-unit id="_msg119" approved="yes">
<source xml:space="preserve">%1 behind</source>
<target xml:space="preserve">%1 behind</target>
- <context-group purpose="location"><context context-type="linenumber">1062</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1077</context></context-group>
</trans-unit>
- <trans-unit id="_msg121">
+ <trans-unit id="_msg120">
<source xml:space="preserve">Catching up…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1067</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1082</context></context-group>
</trans-unit>
- <trans-unit id="_msg122" approved="yes">
+ <trans-unit id="_msg121" approved="yes">
<source xml:space="preserve">Last received block was generated %1 ago.</source>
<target xml:space="preserve">Last received block was generated %1 ago.</target>
- <context-group purpose="location"><context context-type="linenumber">1086</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1101</context></context-group>
</trans-unit>
- <trans-unit id="_msg123" approved="yes">
+ <trans-unit id="_msg122" approved="yes">
<source xml:space="preserve">Transactions after this will not yet be visible.</source>
<target xml:space="preserve">Transactions after this will not yet be visible.</target>
- <context-group purpose="location"><context context-type="linenumber">1088</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1103</context></context-group>
</trans-unit>
- <trans-unit id="_msg124" approved="yes">
+ <trans-unit id="_msg123" approved="yes">
<source xml:space="preserve">Error</source>
<target xml:space="preserve">Error</target>
- <context-group purpose="location"><context context-type="linenumber">1113</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1128</context></context-group>
</trans-unit>
- <trans-unit id="_msg125" approved="yes">
+ <trans-unit id="_msg124" approved="yes">
<source xml:space="preserve">Warning</source>
<target xml:space="preserve">Warning</target>
- <context-group purpose="location"><context context-type="linenumber">1117</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1132</context></context-group>
</trans-unit>
- <trans-unit id="_msg126" approved="yes">
+ <trans-unit id="_msg125" approved="yes">
<source xml:space="preserve">Information</source>
<target xml:space="preserve">Information</target>
- <context-group purpose="location"><context context-type="linenumber">1121</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1136</context></context-group>
</trans-unit>
- <trans-unit id="_msg127" approved="yes">
+ <trans-unit id="_msg126" approved="yes">
<source xml:space="preserve">Up to date</source>
<target xml:space="preserve">Up to date</target>
- <context-group purpose="location"><context context-type="linenumber">1043</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1058</context></context-group>
</trans-unit>
- <trans-unit id="_msg128">
+ <trans-unit id="_msg127">
<source xml:space="preserve">Load Partially Signed Bitcoin Transaction</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">328</context></context-group>
</trans-unit>
- <trans-unit id="_msg129">
+ <trans-unit id="_msg128">
<source xml:space="preserve">Load Partially Signed Bitcoin Transaction from clipboard</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">330</context></context-group>
</trans-unit>
- <trans-unit id="_msg130">
+ <trans-unit id="_msg129">
<source xml:space="preserve">Node window</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">332</context></context-group>
</trans-unit>
- <trans-unit id="_msg131">
+ <trans-unit id="_msg130">
<source xml:space="preserve">Open node debugging and diagnostic console</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">333</context></context-group>
</trans-unit>
- <trans-unit id="_msg132">
+ <trans-unit id="_msg131">
<source xml:space="preserve">&amp;Sending addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">338</context></context-group>
</trans-unit>
- <trans-unit id="_msg133">
+ <trans-unit id="_msg132">
<source xml:space="preserve">&amp;Receiving addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">340</context></context-group>
</trans-unit>
- <trans-unit id="_msg134">
+ <trans-unit id="_msg133">
<source xml:space="preserve">Open a bitcoin: URI</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">344</context></context-group>
</trans-unit>
- <trans-unit id="_msg135">
+ <trans-unit id="_msg134">
<source xml:space="preserve">Open Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">346</context></context-group>
</trans-unit>
- <trans-unit id="_msg136">
+ <trans-unit id="_msg135">
<source xml:space="preserve">Open a wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">348</context></context-group>
</trans-unit>
- <trans-unit id="_msg137">
+ <trans-unit id="_msg136">
<source xml:space="preserve">Close wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">352</context></context-group>
</trans-unit>
- <trans-unit id="_msg138">
+ <trans-unit id="_msg137">
<source xml:space="preserve">Close all wallets</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">359</context></context-group>
</trans-unit>
- <trans-unit id="_msg139">
+ <trans-unit id="_msg138">
<source xml:space="preserve">Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">363</context></context-group>
</trans-unit>
- <trans-unit id="_msg140">
+ <trans-unit id="_msg139">
<source xml:space="preserve">&amp;Mask values</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">365</context></context-group>
</trans-unit>
- <trans-unit id="_msg141">
+ <trans-unit id="_msg140">
<source xml:space="preserve">Mask the values in the Overview tab</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">367</context></context-group>
</trans-unit>
- <trans-unit id="_msg142">
+ <trans-unit id="_msg141">
<source xml:space="preserve">default wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">399</context></context-group>
</trans-unit>
- <trans-unit id="_msg143">
+ <trans-unit id="_msg142">
<source xml:space="preserve">No wallets available</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">420</context></context-group>
</trans-unit>
- <trans-unit id="_msg144">
+ <trans-unit id="_msg143">
<source xml:space="preserve">&amp;Window</source>
<target xml:space="preserve" state="needs-review-translation">&amp;Window</target>
<context-group purpose="location"><context context-type="linenumber">484</context></context-group>
</trans-unit>
- <trans-unit id="_msg145">
+ <trans-unit id="_msg144">
<source xml:space="preserve">Minimize</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">486</context></context-group>
</trans-unit>
- <trans-unit id="_msg146">
+ <trans-unit id="_msg145">
<source xml:space="preserve">Zoom</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">496</context></context-group>
</trans-unit>
- <trans-unit id="_msg147">
+ <trans-unit id="_msg146">
<source xml:space="preserve">Main Window</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">514</context></context-group>
</trans-unit>
- <trans-unit id="_msg148">
+ <trans-unit id="_msg147">
<source xml:space="preserve">%1 client</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">762</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">777</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
- <context-group purpose="location"><context context-type="linenumber">920</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">935</context></context-group>
<note annotates="source" from="developer">A substring of the tooltip.</note>
- <trans-unit id="_msg149[0]">
+ <trans-unit id="_msg148[0]">
<source xml:space="preserve">%n active connection(s) to Bitcoin network.</source>
<target xml:space="preserve"></target>
</trans-unit>
- <trans-unit id="_msg149[1]">
+ <trans-unit id="_msg148[1]">
<source xml:space="preserve">%n active connection(s) to Bitcoin network.</source>
<target xml:space="preserve"></target>
</trans-unit>
</group>
- <trans-unit id="_msg150">
+ <trans-unit id="_msg149">
<source xml:space="preserve">Click for more actions.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">930</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">945</context></context-group>
<note annotates="source" from="developer">A substring of the tooltip. &quot;More actions&quot; are available via the context menu.</note>
</trans-unit>
- <trans-unit id="_msg151">
+ <trans-unit id="_msg150">
<source xml:space="preserve">Show Peers tab</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">947</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">962</context></context-group>
<note annotates="source" from="developer">A context menu item. The &quot;Peers tab&quot; is an element of the &quot;Node window&quot;.</note>
</trans-unit>
- <trans-unit id="_msg152">
+ <trans-unit id="_msg151">
<source xml:space="preserve">Disable network activity</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">955</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">970</context></context-group>
<note annotates="source" from="developer">A context menu item.</note>
</trans-unit>
- <trans-unit id="_msg153">
+ <trans-unit id="_msg152">
<source xml:space="preserve">Enable network activity</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">957</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">972</context></context-group>
<note annotates="source" from="developer">A context menu item. The network activity was disabled previously.</note>
</trans-unit>
- <trans-unit id="_msg154">
+ <trans-unit id="_msg153">
<source xml:space="preserve">Error: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1114</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1129</context></context-group>
</trans-unit>
- <trans-unit id="_msg155">
+ <trans-unit id="_msg154">
<source xml:space="preserve">Warning: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1118</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1133</context></context-group>
</trans-unit>
- <trans-unit id="_msg156">
+ <trans-unit id="_msg155">
<source xml:space="preserve">Date: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1228</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1241</context></context-group>
</trans-unit>
- <trans-unit id="_msg157">
+ <trans-unit id="_msg156">
<source xml:space="preserve">Amount: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1229</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1242</context></context-group>
</trans-unit>
- <trans-unit id="_msg158">
+ <trans-unit id="_msg157">
<source xml:space="preserve">Wallet: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1231</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1244</context></context-group>
</trans-unit>
- <trans-unit id="_msg159">
+ <trans-unit id="_msg158">
<source xml:space="preserve">Type: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1233</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1246</context></context-group>
</trans-unit>
- <trans-unit id="_msg160">
+ <trans-unit id="_msg159">
<source xml:space="preserve">Label: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1235</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1248</context></context-group>
</trans-unit>
- <trans-unit id="_msg161">
+ <trans-unit id="_msg160">
<source xml:space="preserve">Address: %1
</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1237</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1250</context></context-group>
</trans-unit>
- <trans-unit id="_msg162" approved="yes">
+ <trans-unit id="_msg161" approved="yes">
<source xml:space="preserve">Sent transaction</source>
<target xml:space="preserve">Sent transaction</target>
- <context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1251</context></context-group>
</trans-unit>
- <trans-unit id="_msg163" approved="yes">
+ <trans-unit id="_msg162" approved="yes">
<source xml:space="preserve">Incoming transaction</source>
<target xml:space="preserve">Incoming transaction</target>
- <context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1251</context></context-group>
</trans-unit>
- <trans-unit id="_msg164">
+ <trans-unit id="_msg163">
<source xml:space="preserve">HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1303</context></context-group>
</trans-unit>
- <trans-unit id="_msg165">
+ <trans-unit id="_msg164">
<source xml:space="preserve">HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1303</context></context-group>
</trans-unit>
- <trans-unit id="_msg166">
+ <trans-unit id="_msg165">
<source xml:space="preserve">Private key &lt;b&gt;disabled&lt;/b&gt;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1303</context></context-group>
</trans-unit>
- <trans-unit id="_msg167" approved="yes">
+ <trans-unit id="_msg166" approved="yes">
<source xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<target xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</target>
- <context-group purpose="location"><context context-type="linenumber">1309</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1322</context></context-group>
</trans-unit>
- <trans-unit id="_msg168" approved="yes">
+ <trans-unit id="_msg167" approved="yes">
<source xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<target xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</target>
- <context-group purpose="location"><context context-type="linenumber">1317</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1330</context></context-group>
</trans-unit>
- <trans-unit id="_msg169">
+ <trans-unit id="_msg168">
<source xml:space="preserve">Original message:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1437</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1450</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="UnitDisplayStatusBarControl">
- <trans-unit id="_msg170">
+ <trans-unit id="_msg169">
<source xml:space="preserve">Unit to show amounts in. Click to select another unit.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1478</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1491</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/coincontroldialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="CoinControlDialog">
- <trans-unit id="_msg171">
+ <trans-unit id="_msg170">
<source xml:space="preserve">Coin Selection</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg172">
+ <trans-unit id="_msg171">
<source xml:space="preserve">Quantity:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">48</context></context-group>
</trans-unit>
- <trans-unit id="_msg173">
+ <trans-unit id="_msg172">
<source xml:space="preserve">Bytes:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">77</context></context-group>
</trans-unit>
- <trans-unit id="_msg174">
+ <trans-unit id="_msg173">
<source xml:space="preserve">Amount:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">122</context></context-group>
</trans-unit>
- <trans-unit id="_msg175">
+ <trans-unit id="_msg174">
<source xml:space="preserve">Fee:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">202</context></context-group>
</trans-unit>
- <trans-unit id="_msg176">
+ <trans-unit id="_msg175">
<source xml:space="preserve">Dust:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">154</context></context-group>
</trans-unit>
- <trans-unit id="_msg177">
+ <trans-unit id="_msg176">
<source xml:space="preserve">After Fee:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">247</context></context-group>
</trans-unit>
- <trans-unit id="_msg178">
+ <trans-unit id="_msg177">
<source xml:space="preserve">Change:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">279</context></context-group>
</trans-unit>
- <trans-unit id="_msg179">
+ <trans-unit id="_msg178">
<source xml:space="preserve">(un)select all</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">335</context></context-group>
</trans-unit>
- <trans-unit id="_msg180">
+ <trans-unit id="_msg179">
<source xml:space="preserve">Tree mode</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">351</context></context-group>
</trans-unit>
- <trans-unit id="_msg181">
+ <trans-unit id="_msg180">
<source xml:space="preserve">List mode</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">364</context></context-group>
</trans-unit>
- <trans-unit id="_msg182">
+ <trans-unit id="_msg181">
<source xml:space="preserve">Amount</source>
<target xml:space="preserve" state="needs-review-translation">Amount</target>
<context-group purpose="location"><context context-type="linenumber">420</context></context-group>
</trans-unit>
- <trans-unit id="_msg183">
+ <trans-unit id="_msg182">
<source xml:space="preserve">Received with label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">425</context></context-group>
</trans-unit>
- <trans-unit id="_msg184">
+ <trans-unit id="_msg183">
<source xml:space="preserve">Received with address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">430</context></context-group>
</trans-unit>
- <trans-unit id="_msg185">
+ <trans-unit id="_msg184">
<source xml:space="preserve">Date</source>
<target xml:space="preserve" state="needs-review-translation">Date</target>
<context-group purpose="location"><context context-type="linenumber">435</context></context-group>
</trans-unit>
- <trans-unit id="_msg186">
+ <trans-unit id="_msg185">
<source xml:space="preserve">Confirmations</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">440</context></context-group>
</trans-unit>
- <trans-unit id="_msg187">
+ <trans-unit id="_msg186">
<source xml:space="preserve">Confirmed</source>
<target xml:space="preserve" state="needs-review-translation">Confirmed</target>
<context-group purpose="location"><context context-type="linenumber">443</context></context-group>
@@ -1012,34 +1008,38 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../coincontroldialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="CoinControlDialog">
+ <trans-unit id="_msg187">
+ <source xml:space="preserve">Copy amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ </trans-unit>
<trans-unit id="_msg188">
- <source xml:space="preserve">Copy address</source>
+ <source xml:space="preserve">&amp;Copy address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">55</context></context-group>
</trans-unit>
<trans-unit id="_msg189">
- <source xml:space="preserve">Copy label</source>
+ <source xml:space="preserve">Copy &amp;label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">56</context></context-group>
</trans-unit>
<trans-unit id="_msg190">
- <source xml:space="preserve">Copy amount</source>
+ <source xml:space="preserve">Copy &amp;amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">57</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
</trans-unit>
<trans-unit id="_msg191">
- <source xml:space="preserve">Copy transaction ID</source>
+ <source xml:space="preserve">Copy transaction &amp;ID</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">58</context></context-group>
</trans-unit>
<trans-unit id="_msg192">
- <source xml:space="preserve">Lock unspent</source>
+ <source xml:space="preserve">L&amp;ock unspent</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">60</context></context-group>
</trans-unit>
<trans-unit id="_msg193">
- <source xml:space="preserve">Unlock unspent</source>
+ <source xml:space="preserve">&amp;Unlock unspent</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">61</context></context-group>
</trans-unit>
@@ -1121,170 +1121,191 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<trans-unit id="_msg208">
<source xml:space="preserve">Creating Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">254</context></context-group>
</trans-unit>
<trans-unit id="_msg209">
<source xml:space="preserve">Create wallet failed</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">281</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
</trans-unit>
<trans-unit id="_msg210">
<source xml:space="preserve">Create wallet warning</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg211">
+ <source xml:space="preserve">Can&apos;t list signers</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">303</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="OpenWalletActivity">
- <trans-unit id="_msg211">
+ <trans-unit id="_msg212">
<source xml:space="preserve">Open wallet failed</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
</trans-unit>
- <trans-unit id="_msg212">
+ <trans-unit id="_msg213">
<source xml:space="preserve">Open wallet warning</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">337</context></context-group>
</trans-unit>
- <trans-unit id="_msg213">
+ <trans-unit id="_msg214">
<source xml:space="preserve">default wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">347</context></context-group>
</trans-unit>
- <trans-unit id="_msg214">
+ <trans-unit id="_msg215">
<source xml:space="preserve">Opening Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">336</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">349</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="WalletController">
- <trans-unit id="_msg215">
- <source xml:space="preserve">Close wallet</source>
- <target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
- </trans-unit>
<trans-unit id="_msg216">
- <source xml:space="preserve">Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <source xml:space="preserve">Close wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">87</context></context-group>
</trans-unit>
<trans-unit id="_msg217">
- <source xml:space="preserve">Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <source xml:space="preserve">Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">88</context></context-group>
</trans-unit>
<trans-unit id="_msg218">
- <source xml:space="preserve">Close all wallets</source>
+ <source xml:space="preserve">Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">89</context></context-group>
</trans-unit>
<trans-unit id="_msg219">
- <source xml:space="preserve">Are you sure you wish to close all wallets?</source>
+ <source xml:space="preserve">Close all wallets</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">102</context></context-group>
</trans-unit>
+ <trans-unit id="_msg220">
+ <source xml:space="preserve">Are you sure you wish to close all wallets?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
+ </trans-unit>
</group>
</body></file>
<file original="../forms/createwalletdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="CreateWalletDialog">
- <trans-unit id="_msg220">
+ <trans-unit id="_msg221">
<source xml:space="preserve">Create Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg221">
+ <trans-unit id="_msg222">
<source xml:space="preserve">Wallet Name</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">25</context></context-group>
</trans-unit>
- <trans-unit id="_msg222">
+ <trans-unit id="_msg223">
<source xml:space="preserve">Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">38</context></context-group>
</trans-unit>
- <trans-unit id="_msg223">
+ <trans-unit id="_msg224">
<source xml:space="preserve">Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">47</context></context-group>
</trans-unit>
- <trans-unit id="_msg224">
+ <trans-unit id="_msg225">
<source xml:space="preserve">Encrypt Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">50</context></context-group>
</trans-unit>
- <trans-unit id="_msg225">
+ <trans-unit id="_msg226">
<source xml:space="preserve">Advanced Options</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">76</context></context-group>
</trans-unit>
- <trans-unit id="_msg226">
+ <trans-unit id="_msg227">
<source xml:space="preserve">Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">85</context></context-group>
</trans-unit>
- <trans-unit id="_msg227">
+ <trans-unit id="_msg228">
<source xml:space="preserve">Disable Private Keys</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">88</context></context-group>
</trans-unit>
- <trans-unit id="_msg228">
+ <trans-unit id="_msg229">
<source xml:space="preserve">Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">95</context></context-group>
</trans-unit>
- <trans-unit id="_msg229">
+ <trans-unit id="_msg230">
<source xml:space="preserve">Make Blank Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">98</context></context-group>
</trans-unit>
- <trans-unit id="_msg230">
+ <trans-unit id="_msg231">
<source xml:space="preserve">Use descriptors for scriptPubKey management</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">105</context></context-group>
</trans-unit>
- <trans-unit id="_msg231">
+ <trans-unit id="_msg232">
<source xml:space="preserve">Descriptor Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">108</context></context-group>
</trans-unit>
+ <trans-unit id="_msg233">
+ <source xml:space="preserve">Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">115</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg234">
+ <source xml:space="preserve">External signer</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">118</context></context-group>
+ </trans-unit>
</group>
</body></file>
<file original="../createwalletdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="CreateWalletDialog">
- <trans-unit id="_msg232">
+ <trans-unit id="_msg235">
<source xml:space="preserve">Create</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">21</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">22</context></context-group>
</trans-unit>
- <trans-unit id="_msg233">
+ <trans-unit id="_msg236">
<source xml:space="preserve">Compiled without sqlite support (required for descriptor wallets)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">63</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg237">
+ <source xml:space="preserve">Compiled without external signing support (required for external signing)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">104</context></context-group>
+ <note annotates="source" from="developer">&quot;External signing&quot; means using devices such as hardware wallets.</note>
</trans-unit>
</group>
</body></file>
<file original="../forms/editaddressdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="EditAddressDialog">
- <trans-unit id="_msg234" approved="yes">
+ <trans-unit id="_msg238" approved="yes">
<source xml:space="preserve">Edit Address</source>
<target xml:space="preserve">Edit Address</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg235" approved="yes">
+ <trans-unit id="_msg239" approved="yes">
<source xml:space="preserve">&amp;Label</source>
<target xml:space="preserve">&amp;Label</target>
<context-group purpose="location"><context context-type="linenumber">25</context></context-group>
</trans-unit>
- <trans-unit id="_msg236">
+ <trans-unit id="_msg240">
<source xml:space="preserve">The label associated with this address list entry</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">35</context></context-group>
</trans-unit>
- <trans-unit id="_msg237">
+ <trans-unit id="_msg241">
<source xml:space="preserve">The address associated with this address list entry. This can only be modified for sending addresses.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">52</context></context-group>
</trans-unit>
- <trans-unit id="_msg238" approved="yes">
+ <trans-unit id="_msg242" approved="yes">
<source xml:space="preserve">&amp;Address</source>
<target xml:space="preserve">&amp;Address</target>
<context-group purpose="location"><context context-type="linenumber">42</context></context-group>
@@ -1293,42 +1314,42 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../editaddressdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="EditAddressDialog">
- <trans-unit id="_msg239">
+ <trans-unit id="_msg243">
<source xml:space="preserve">New sending address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">29</context></context-group>
</trans-unit>
- <trans-unit id="_msg240">
+ <trans-unit id="_msg244">
<source xml:space="preserve">Edit receiving address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">32</context></context-group>
</trans-unit>
- <trans-unit id="_msg241">
+ <trans-unit id="_msg245">
<source xml:space="preserve">Edit sending address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">36</context></context-group>
</trans-unit>
- <trans-unit id="_msg242">
+ <trans-unit id="_msg246">
<source xml:space="preserve">The entered address &quot;%1&quot; is not a valid Bitcoin address.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">113</context></context-group>
</trans-unit>
- <trans-unit id="_msg243">
+ <trans-unit id="_msg247">
<source xml:space="preserve">Address &quot;%1&quot; already exists as a receiving address with label &quot;%2&quot; and so cannot be added as a sending address.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">146</context></context-group>
</trans-unit>
- <trans-unit id="_msg244">
+ <trans-unit id="_msg248">
<source xml:space="preserve">The entered address &quot;%1&quot; is already in the address book with label &quot;%2&quot;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">151</context></context-group>
</trans-unit>
- <trans-unit id="_msg245">
+ <trans-unit id="_msg249">
<source xml:space="preserve">Could not unlock wallet.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">123</context></context-group>
</trans-unit>
- <trans-unit id="_msg246">
+ <trans-unit id="_msg250">
<source xml:space="preserve">New key generation failed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">128</context></context-group>
@@ -1337,59 +1358,59 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../intro.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="FreespaceChecker">
- <trans-unit id="_msg247" approved="yes">
+ <trans-unit id="_msg251" approved="yes">
<source xml:space="preserve">A new data directory will be created.</source>
<target xml:space="preserve">A new data directory will be created.</target>
<context-group purpose="location"><context context-type="linenumber">73</context></context-group>
</trans-unit>
- <trans-unit id="_msg248" approved="yes">
+ <trans-unit id="_msg252" approved="yes">
<source xml:space="preserve">name</source>
<target xml:space="preserve">name</target>
<context-group purpose="location"><context context-type="linenumber">95</context></context-group>
</trans-unit>
- <trans-unit id="_msg249" approved="yes">
+ <trans-unit id="_msg253" approved="yes">
<source xml:space="preserve">Directory already exists. Add %1 if you intend to create a new directory here.</source>
<target xml:space="preserve">Directory already exists. Add %1 if you intend to create a new directory here.</target>
<context-group purpose="location"><context context-type="linenumber">97</context></context-group>
</trans-unit>
- <trans-unit id="_msg250" approved="yes">
+ <trans-unit id="_msg254" approved="yes">
<source xml:space="preserve">Path already exists, and is not a directory.</source>
<target xml:space="preserve">Path already exists, and is not a directory.</target>
<context-group purpose="location"><context context-type="linenumber">100</context></context-group>
</trans-unit>
- <trans-unit id="_msg251" approved="yes">
+ <trans-unit id="_msg255" approved="yes">
<source xml:space="preserve">Cannot create data directory here.</source>
<target xml:space="preserve">Cannot create data directory here.</target>
<context-group purpose="location"><context context-type="linenumber">107</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="Intro">
- <trans-unit id="_msg252">
+ <trans-unit id="_msg256">
<source xml:space="preserve">Bitcoin</source>
<target xml:space="preserve" state="needs-review-translation">Bitcoin</target>
<context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
- <trans-unit id="_msg253">
+ <trans-unit id="_msg257">
<source xml:space="preserve">%1 GB of free space available</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">301</context></context-group>
</trans-unit>
- <trans-unit id="_msg254">
+ <trans-unit id="_msg258">
<source xml:space="preserve">(of %1 GB needed)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">303</context></context-group>
</trans-unit>
- <trans-unit id="_msg255">
+ <trans-unit id="_msg259">
<source xml:space="preserve">(%1 GB needed for full chain)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">306</context></context-group>
</trans-unit>
- <trans-unit id="_msg256">
+ <trans-unit id="_msg260">
<source xml:space="preserve">At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">378</context></context-group>
</trans-unit>
- <trans-unit id="_msg257">
+ <trans-unit id="_msg261">
<source xml:space="preserve">Approximately %1 GB of data will be stored in this directory.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">381</context></context-group>
@@ -1397,31 +1418,31 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">390</context></context-group>
<note annotates="source" from="developer">Explanatory text on the capability of the current prune target.</note>
- <trans-unit id="_msg258[0]">
+ <trans-unit id="_msg262[0]">
<source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source>
<target xml:space="preserve"></target>
</trans-unit>
- <trans-unit id="_msg258[1]">
+ <trans-unit id="_msg262[1]">
<source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source>
<target xml:space="preserve"></target>
</trans-unit>
</group>
- <trans-unit id="_msg259">
+ <trans-unit id="_msg263">
<source xml:space="preserve">%1 will download and store a copy of the Bitcoin block chain.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">392</context></context-group>
</trans-unit>
- <trans-unit id="_msg260">
+ <trans-unit id="_msg264">
<source xml:space="preserve">The wallet will also be stored in this directory.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">394</context></context-group>
</trans-unit>
- <trans-unit id="_msg261">
+ <trans-unit id="_msg265">
<source xml:space="preserve">Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">250</context></context-group>
</trans-unit>
- <trans-unit id="_msg262" approved="yes">
+ <trans-unit id="_msg266" approved="yes">
<source xml:space="preserve">Error</source>
<target xml:space="preserve">Error</target>
<context-group purpose="location"><context context-type="linenumber">280</context></context-group>
@@ -1430,29 +1451,29 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../utilitydialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="HelpMessageDialog">
- <trans-unit id="_msg263">
+ <trans-unit id="_msg267">
<source xml:space="preserve">version</source>
<target xml:space="preserve" state="needs-review-translation">version</target>
<context-group purpose="location"><context context-type="linenumber">37</context></context-group>
</trans-unit>
- <trans-unit id="_msg264">
+ <trans-unit id="_msg268">
<source xml:space="preserve">About %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">41</context></context-group>
</trans-unit>
- <trans-unit id="_msg265">
+ <trans-unit id="_msg269">
<source xml:space="preserve">Command-line options</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">60</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="ShutdownWindow">
- <trans-unit id="_msg266">
+ <trans-unit id="_msg270">
<source xml:space="preserve">%1 is shutting down…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">145</context></context-group>
</trans-unit>
- <trans-unit id="_msg267">
+ <trans-unit id="_msg271">
<source xml:space="preserve">Do not shut down the computer until this window disappears.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">146</context></context-group>
@@ -1461,57 +1482,57 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../forms/intro.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="Intro">
- <trans-unit id="_msg268" approved="yes">
+ <trans-unit id="_msg272" approved="yes">
<source xml:space="preserve">Welcome</source>
<target xml:space="preserve">Welcome</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg269">
+ <trans-unit id="_msg273">
<source xml:space="preserve">Welcome to %1.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">23</context></context-group>
</trans-unit>
- <trans-unit id="_msg270">
+ <trans-unit id="_msg274">
<source xml:space="preserve">As this is the first time the program is launched, you can choose where %1 will store its data.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
- <trans-unit id="_msg271">
+ <trans-unit id="_msg275">
<source xml:space="preserve">When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">206</context></context-group>
</trans-unit>
- <trans-unit id="_msg272">
+ <trans-unit id="_msg276">
<source xml:space="preserve">Limit block chain storage to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">238</context></context-group>
</trans-unit>
- <trans-unit id="_msg273">
+ <trans-unit id="_msg277">
<source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">241</context></context-group>
</trans-unit>
- <trans-unit id="_msg274">
+ <trans-unit id="_msg278">
<source xml:space="preserve"> GB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">248</context></context-group>
</trans-unit>
- <trans-unit id="_msg275">
+ <trans-unit id="_msg279">
<source xml:space="preserve">This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">216</context></context-group>
</trans-unit>
- <trans-unit id="_msg276">
+ <trans-unit id="_msg280">
<source xml:space="preserve">If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
</trans-unit>
- <trans-unit id="_msg277" approved="yes">
+ <trans-unit id="_msg281" approved="yes">
<source xml:space="preserve">Use the default data directory</source>
<target xml:space="preserve">Use the default data directory</target>
<context-group purpose="location"><context context-type="linenumber">66</context></context-group>
</trans-unit>
- <trans-unit id="_msg278" approved="yes">
+ <trans-unit id="_msg282" approved="yes">
<source xml:space="preserve">Use a custom data directory:</source>
<target xml:space="preserve">Use a custom data directory:</target>
<context-group purpose="location"><context context-type="linenumber">73</context></context-group>
@@ -1520,65 +1541,65 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../forms/modaloverlay.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ModalOverlay">
- <trans-unit id="_msg279">
+ <trans-unit id="_msg283">
<source xml:space="preserve">Form</source>
<target xml:space="preserve" state="needs-review-translation">Form</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg280">
+ <trans-unit id="_msg284">
<source xml:space="preserve">Recent transactions may not yet be visible, and therefore your wallet&apos;s balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">133</context></context-group>
</trans-unit>
- <trans-unit id="_msg281">
+ <trans-unit id="_msg285">
<source xml:space="preserve">Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg282">
+ <trans-unit id="_msg286">
<source xml:space="preserve">Number of blocks left</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg283">
+ <trans-unit id="_msg287">
<source xml:space="preserve">Unknown…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">222</context></context-group>
<context-group purpose="location"><context context-type="linenumber">248</context></context-group>
<context-group purpose="location"><context context-type="sourcefile">../modaloverlay.cpp</context><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg284">
+ <trans-unit id="_msg288">
<source xml:space="preserve">calculating…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">292</context></context-group>
<context-group purpose="location"><context context-type="linenumber">312</context></context-group>
</trans-unit>
- <trans-unit id="_msg285">
+ <trans-unit id="_msg289">
<source xml:space="preserve">Last block time</source>
<target xml:space="preserve" state="needs-review-translation">Last block time</target>
<context-group purpose="location"><context context-type="linenumber">235</context></context-group>
</trans-unit>
- <trans-unit id="_msg286">
+ <trans-unit id="_msg290">
<source xml:space="preserve">Progress</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">261</context></context-group>
</trans-unit>
- <trans-unit id="_msg287">
+ <trans-unit id="_msg291">
<source xml:space="preserve">Progress increase per hour</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">285</context></context-group>
</trans-unit>
- <trans-unit id="_msg288">
+ <trans-unit id="_msg292">
<source xml:space="preserve">Estimated time left until synced</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
- <trans-unit id="_msg289">
+ <trans-unit id="_msg293">
<source xml:space="preserve">Hide</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">342</context></context-group>
</trans-unit>
- <trans-unit id="_msg290">
+ <trans-unit id="_msg294">
<source xml:space="preserve">Esc</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">345</context></context-group>
@@ -1587,19 +1608,19 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../modaloverlay.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ModalOverlay">
- <trans-unit id="_msg291">
+ <trans-unit id="_msg295">
<source xml:space="preserve">%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">34</context></context-group>
</trans-unit>
- <trans-unit id="_msg292">
+ <trans-unit id="_msg296">
<source xml:space="preserve">Unknown. Syncing Headers (%1, %2%)…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">158</context></context-group>
</trans-unit>
</group>
<group restype="x-trolltech-linguist-context" resname="QObject">
- <trans-unit id="_msg293">
+ <trans-unit id="_msg297">
<source xml:space="preserve">unknown</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">123</context></context-group>
@@ -1608,12 +1629,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../forms/openuridialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="OpenURIDialog">
- <trans-unit id="_msg294">
+ <trans-unit id="_msg298">
<source xml:space="preserve">Open bitcoin URI</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg295">
+ <trans-unit id="_msg299">
<source xml:space="preserve">URI:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">22</context></context-group>
@@ -1622,474 +1643,495 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../forms/optionsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="OptionsDialog">
- <trans-unit id="_msg296" approved="yes">
+ <trans-unit id="_msg300" approved="yes">
<source xml:space="preserve">Options</source>
<target xml:space="preserve">Options</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg297" approved="yes">
+ <trans-unit id="_msg301" approved="yes">
<source xml:space="preserve">&amp;Main</source>
<target xml:space="preserve">&amp;Main</target>
<context-group purpose="location"><context context-type="linenumber">27</context></context-group>
</trans-unit>
- <trans-unit id="_msg298">
+ <trans-unit id="_msg302">
<source xml:space="preserve">Automatically start %1 after logging in to the system.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">33</context></context-group>
</trans-unit>
- <trans-unit id="_msg299">
+ <trans-unit id="_msg303">
<source xml:space="preserve">&amp;Start %1 on system login</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">36</context></context-group>
</trans-unit>
- <trans-unit id="_msg300">
+ <trans-unit id="_msg304">
<source xml:space="preserve">Enabling pruning significantly reduces the disk space required to store transactions. All blocks are still fully validated. Reverting this setting requires re-downloading the entire blockchain.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">58</context></context-group>
</trans-unit>
- <trans-unit id="_msg301">
+ <trans-unit id="_msg305">
<source xml:space="preserve">Size of &amp;database cache</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">108</context></context-group>
</trans-unit>
- <trans-unit id="_msg302">
+ <trans-unit id="_msg306">
<source xml:space="preserve">Number of script &amp;verification threads</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">151</context></context-group>
</trans-unit>
- <trans-unit id="_msg303">
+ <trans-unit id="_msg307">
<source xml:space="preserve">IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">352</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">539</context></context-group>
</trans-unit>
- <trans-unit id="_msg304">
+ <trans-unit id="_msg308">
<source xml:space="preserve">Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">391</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">414</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">437</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">421</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">444</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">467</context></context-group>
</trans-unit>
- <trans-unit id="_msg305">
+ <trans-unit id="_msg309">
<source xml:space="preserve">Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">606</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">636</context></context-group>
</trans-unit>
- <trans-unit id="_msg306">
+ <trans-unit id="_msg310">
<source xml:space="preserve">Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">686</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">699</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">716</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">729</context></context-group>
</trans-unit>
- <trans-unit id="_msg307">
+ <trans-unit id="_msg311">
<source xml:space="preserve">Open the %1 configuration file from the working directory.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">878</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">908</context></context-group>
</trans-unit>
- <trans-unit id="_msg308">
+ <trans-unit id="_msg312">
<source xml:space="preserve">Open Configuration File</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">881</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">911</context></context-group>
</trans-unit>
- <trans-unit id="_msg309" approved="yes">
+ <trans-unit id="_msg313" approved="yes">
<source xml:space="preserve">Reset all client options to default.</source>
<target xml:space="preserve">Reset all client options to default.</target>
- <context-group purpose="location"><context context-type="linenumber">891</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">921</context></context-group>
</trans-unit>
- <trans-unit id="_msg310" approved="yes">
+ <trans-unit id="_msg314" approved="yes">
<source xml:space="preserve">&amp;Reset Options</source>
<target xml:space="preserve">&amp;Reset Options</target>
- <context-group purpose="location"><context context-type="linenumber">894</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">924</context></context-group>
</trans-unit>
- <trans-unit id="_msg311" approved="yes">
+ <trans-unit id="_msg315" approved="yes">
<source xml:space="preserve">&amp;Network</source>
<target xml:space="preserve">&amp;Network</target>
- <context-group purpose="location"><context context-type="linenumber">249</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">279</context></context-group>
</trans-unit>
- <trans-unit id="_msg312">
+ <trans-unit id="_msg316">
<source xml:space="preserve">Prune &amp;block storage to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">61</context></context-group>
</trans-unit>
- <trans-unit id="_msg313">
+ <trans-unit id="_msg317">
<source xml:space="preserve">GB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">71</context></context-group>
</trans-unit>
- <trans-unit id="_msg314">
+ <trans-unit id="_msg318">
<source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">96</context></context-group>
</trans-unit>
- <trans-unit id="_msg315">
+ <trans-unit id="_msg319">
<source xml:space="preserve">MiB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">124</context></context-group>
</trans-unit>
- <trans-unit id="_msg316">
+ <trans-unit id="_msg320">
<source xml:space="preserve">(0 = auto, &lt;0 = leave that many cores free)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">164</context></context-group>
</trans-unit>
- <trans-unit id="_msg317">
+ <trans-unit id="_msg321">
<source xml:space="preserve">W&amp;allet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">200</context></context-group>
</trans-unit>
- <trans-unit id="_msg318">
+ <trans-unit id="_msg322">
<source xml:space="preserve">Expert</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">206</context></context-group>
</trans-unit>
- <trans-unit id="_msg319">
+ <trans-unit id="_msg323">
<source xml:space="preserve">Enable coin &amp;control features</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg320">
+ <trans-unit id="_msg324">
<source xml:space="preserve">If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">222</context></context-group>
</trans-unit>
- <trans-unit id="_msg321">
+ <trans-unit id="_msg325">
<source xml:space="preserve">&amp;Spend unconfirmed change</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">225</context></context-group>
</trans-unit>
- <trans-unit id="_msg322" approved="yes">
+ <trans-unit id="_msg326">
+ <source xml:space="preserve">External Signer (e.g. hardware wallet)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">235</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg327">
+ <source xml:space="preserve">&amp;External signer script path</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">243</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg328">
+ <source xml:space="preserve">Full path to a Bitcoin Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg329" approved="yes">
<source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
<target xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</target>
- <context-group purpose="location"><context context-type="linenumber">255</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
</trans-unit>
- <trans-unit id="_msg323" approved="yes">
+ <trans-unit id="_msg330" approved="yes">
<source xml:space="preserve">Map port using &amp;UPnP</source>
<target xml:space="preserve">Map port using &amp;UPnP</target>
- <context-group purpose="location"><context context-type="linenumber">258</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">288</context></context-group>
</trans-unit>
- <trans-unit id="_msg324">
+ <trans-unit id="_msg331">
<source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">265</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">295</context></context-group>
</trans-unit>
- <trans-unit id="_msg325">
+ <trans-unit id="_msg332">
<source xml:space="preserve">Map port using NA&amp;T-PMP</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
</trans-unit>
- <trans-unit id="_msg326">
+ <trans-unit id="_msg333">
<source xml:space="preserve">Accept connections from outside.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
- <trans-unit id="_msg327">
+ <trans-unit id="_msg334">
<source xml:space="preserve">Allow incomin&amp;g connections</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">278</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">308</context></context-group>
</trans-unit>
- <trans-unit id="_msg328">
+ <trans-unit id="_msg335">
<source xml:space="preserve">Connect to the Bitcoin network through a SOCKS5 proxy.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">315</context></context-group>
</trans-unit>
- <trans-unit id="_msg329">
+ <trans-unit id="_msg336">
<source xml:space="preserve">&amp;Connect through SOCKS5 proxy (default proxy):</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">288</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">318</context></context-group>
</trans-unit>
- <trans-unit id="_msg330" approved="yes">
+ <trans-unit id="_msg337" approved="yes">
<source xml:space="preserve">Proxy &amp;IP:</source>
<target xml:space="preserve">Proxy &amp;IP:</target>
- <context-group purpose="location"><context context-type="linenumber">297</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">484</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">327</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">514</context></context-group>
</trans-unit>
- <trans-unit id="_msg331" approved="yes">
+ <trans-unit id="_msg338" approved="yes">
<source xml:space="preserve">&amp;Port:</source>
<target xml:space="preserve">&amp;Port:</target>
- <context-group purpose="location"><context context-type="linenumber">329</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">516</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">359</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">546</context></context-group>
</trans-unit>
- <trans-unit id="_msg332" approved="yes">
+ <trans-unit id="_msg339" approved="yes">
<source xml:space="preserve">Port of the proxy (e.g. 9050)</source>
<target xml:space="preserve">Port of the proxy (e.g. 9050)</target>
- <context-group purpose="location"><context context-type="linenumber">354</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">541</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">384</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">571</context></context-group>
</trans-unit>
- <trans-unit id="_msg333">
+ <trans-unit id="_msg340">
<source xml:space="preserve">Used for reaching peers via:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">378</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">408</context></context-group>
</trans-unit>
- <trans-unit id="_msg334">
+ <trans-unit id="_msg341">
<source xml:space="preserve">IPv4</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">401</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">431</context></context-group>
</trans-unit>
- <trans-unit id="_msg335">
+ <trans-unit id="_msg342">
<source xml:space="preserve">IPv6</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">424</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">454</context></context-group>
</trans-unit>
- <trans-unit id="_msg336">
+ <trans-unit id="_msg343">
<source xml:space="preserve">Tor</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">447</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">477</context></context-group>
</trans-unit>
- <trans-unit id="_msg337" approved="yes">
+ <trans-unit id="_msg344" approved="yes">
<source xml:space="preserve">&amp;Window</source>
<target xml:space="preserve">&amp;Window</target>
- <context-group purpose="location"><context context-type="linenumber">577</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">607</context></context-group>
</trans-unit>
- <trans-unit id="_msg338">
+ <trans-unit id="_msg345">
<source xml:space="preserve">Show the icon in the system tray.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">583</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">613</context></context-group>
</trans-unit>
- <trans-unit id="_msg339">
+ <trans-unit id="_msg346">
<source xml:space="preserve">&amp;Show tray icon</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">586</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">616</context></context-group>
</trans-unit>
- <trans-unit id="_msg340" approved="yes">
+ <trans-unit id="_msg347" approved="yes">
<source xml:space="preserve">Show only a tray icon after minimizing the window.</source>
<target xml:space="preserve">Show only a tray icon after minimizing the window.</target>
- <context-group purpose="location"><context context-type="linenumber">596</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">626</context></context-group>
</trans-unit>
- <trans-unit id="_msg341" approved="yes">
+ <trans-unit id="_msg348" approved="yes">
<source xml:space="preserve">&amp;Minimize to the tray instead of the taskbar</source>
<target xml:space="preserve">&amp;Minimize to the tray instead of the taskbar</target>
- <context-group purpose="location"><context context-type="linenumber">599</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">629</context></context-group>
</trans-unit>
- <trans-unit id="_msg342" approved="yes">
+ <trans-unit id="_msg349" approved="yes">
<source xml:space="preserve">M&amp;inimize on close</source>
<target xml:space="preserve">M&amp;inimize on close</target>
- <context-group purpose="location"><context context-type="linenumber">609</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">639</context></context-group>
</trans-unit>
- <trans-unit id="_msg343" approved="yes">
+ <trans-unit id="_msg350" approved="yes">
<source xml:space="preserve">&amp;Display</source>
<target xml:space="preserve">&amp;Display</target>
- <context-group purpose="location"><context context-type="linenumber">630</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">660</context></context-group>
</trans-unit>
- <trans-unit id="_msg344" approved="yes">
+ <trans-unit id="_msg351" approved="yes">
<source xml:space="preserve">User Interface &amp;language:</source>
<target xml:space="preserve">User Interface &amp;language:</target>
- <context-group purpose="location"><context context-type="linenumber">638</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">668</context></context-group>
</trans-unit>
- <trans-unit id="_msg345">
+ <trans-unit id="_msg352">
<source xml:space="preserve">The user interface language can be set here. This setting will take effect after restarting %1.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">651</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">681</context></context-group>
</trans-unit>
- <trans-unit id="_msg346" approved="yes">
+ <trans-unit id="_msg353" approved="yes">
<source xml:space="preserve">&amp;Unit to show amounts in:</source>
<target xml:space="preserve">&amp;Unit to show amounts in:</target>
- <context-group purpose="location"><context context-type="linenumber">662</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">692</context></context-group>
</trans-unit>
- <trans-unit id="_msg347" approved="yes">
+ <trans-unit id="_msg354" approved="yes">
<source xml:space="preserve">Choose the default subdivision unit to show in the interface and when sending coins.</source>
<target xml:space="preserve">Choose the default subdivision unit to show in the interface and when sending coins.</target>
- <context-group purpose="location"><context context-type="linenumber">675</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">705</context></context-group>
</trans-unit>
- <trans-unit id="_msg348">
+ <trans-unit id="_msg355">
<source xml:space="preserve">Whether to show coin control features or not.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">212</context></context-group>
</trans-unit>
- <trans-unit id="_msg349">
+ <trans-unit id="_msg356">
<source xml:space="preserve">Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">472</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">502</context></context-group>
</trans-unit>
- <trans-unit id="_msg350">
+ <trans-unit id="_msg357">
<source xml:space="preserve">Use separate SOCKS&amp;5 proxy to reach peers via Tor onion services:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">475</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
</trans-unit>
- <trans-unit id="_msg351">
+ <trans-unit id="_msg358">
<source xml:space="preserve">&amp;Third party transaction URLs</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">689</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">719</context></context-group>
</trans-unit>
- <trans-unit id="_msg352">
+ <trans-unit id="_msg359">
<source xml:space="preserve">Monospaced font in the Overview tab:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">711</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">741</context></context-group>
</trans-unit>
- <trans-unit id="_msg353">
+ <trans-unit id="_msg360">
<source xml:space="preserve">embedded &quot;%1&quot;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">719</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">749</context></context-group>
</trans-unit>
- <trans-unit id="_msg354">
+ <trans-unit id="_msg361">
<source xml:space="preserve">closest matching &quot;%1&quot;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">768</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">798</context></context-group>
</trans-unit>
- <trans-unit id="_msg355">
+ <trans-unit id="_msg362">
<source xml:space="preserve">Options set in this dialog are overridden by the command line or in the configuration file:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">833</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">863</context></context-group>
</trans-unit>
- <trans-unit id="_msg356" approved="yes">
+ <trans-unit id="_msg363" approved="yes">
<source xml:space="preserve">&amp;OK</source>
<target xml:space="preserve">&amp;OK</target>
- <context-group purpose="location"><context context-type="linenumber">974</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1004</context></context-group>
</trans-unit>
- <trans-unit id="_msg357" approved="yes">
+ <trans-unit id="_msg364" approved="yes">
<source xml:space="preserve">&amp;Cancel</source>
<target xml:space="preserve">&amp;Cancel</target>
- <context-group purpose="location"><context context-type="linenumber">987</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1017</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../optionsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="OptionsDialog">
- <trans-unit id="_msg358" approved="yes">
+ <trans-unit id="_msg365">
+ <source xml:space="preserve">Compiled without external signing support (required for external signing)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ <note annotates="source" from="developer">&quot;External signing&quot; means using devices such as hardware wallets.</note>
+ </trans-unit>
+ <trans-unit id="_msg366" approved="yes">
<source xml:space="preserve">default</source>
<target xml:space="preserve">default</target>
- <context-group purpose="location"><context context-type="linenumber">104</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">109</context></context-group>
</trans-unit>
- <trans-unit id="_msg359">
+ <trans-unit id="_msg367">
<source xml:space="preserve">none</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">185</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">190</context></context-group>
</trans-unit>
- <trans-unit id="_msg360" approved="yes">
+ <trans-unit id="_msg368" approved="yes">
<source xml:space="preserve">Confirm options reset</source>
<target xml:space="preserve">Confirm options reset</target>
- <context-group purpose="location"><context context-type="linenumber">276</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
</trans-unit>
- <trans-unit id="_msg361">
+ <trans-unit id="_msg369">
<source xml:space="preserve">Client restart required to activate changes.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">341</context></context-group>
</trans-unit>
- <trans-unit id="_msg362">
+ <trans-unit id="_msg370">
<source xml:space="preserve">Client will be shut down. Do you want to proceed?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
</trans-unit>
- <trans-unit id="_msg363">
+ <trans-unit id="_msg371">
<source xml:space="preserve">Configuration options</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">299</context></context-group>
</trans-unit>
- <trans-unit id="_msg364">
+ <trans-unit id="_msg372">
<source xml:space="preserve">The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">293</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">300</context></context-group>
</trans-unit>
- <trans-unit id="_msg365">
+ <trans-unit id="_msg373">
<source xml:space="preserve">Error</source>
<target xml:space="preserve" state="needs-review-translation">Error</target>
- <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
- <trans-unit id="_msg366">
+ <trans-unit id="_msg374">
<source xml:space="preserve">The configuration file could not be opened.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
- <trans-unit id="_msg367">
+ <trans-unit id="_msg375">
<source xml:space="preserve">This change would require a client restart.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">345</context></context-group>
</trans-unit>
- <trans-unit id="_msg368" approved="yes">
+ <trans-unit id="_msg376" approved="yes">
<source xml:space="preserve">The supplied proxy address is invalid.</source>
<target xml:space="preserve">The supplied proxy address is invalid.</target>
- <context-group purpose="location"><context context-type="linenumber">366</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">373</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/overviewpage.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="OverviewPage">
- <trans-unit id="_msg369" approved="yes">
+ <trans-unit id="_msg377" approved="yes">
<source xml:space="preserve">Form</source>
<target xml:space="preserve">Form</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg370" approved="yes">
+ <trans-unit id="_msg378" approved="yes">
<source xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
<target xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</target>
<context-group purpose="location"><context context-type="linenumber">76</context></context-group>
<context-group purpose="location"><context context-type="linenumber">411</context></context-group>
</trans-unit>
- <trans-unit id="_msg371">
+ <trans-unit id="_msg379">
<source xml:space="preserve">Watch-only:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">284</context></context-group>
</trans-unit>
- <trans-unit id="_msg372">
+ <trans-unit id="_msg380">
<source xml:space="preserve">Available:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">294</context></context-group>
</trans-unit>
- <trans-unit id="_msg373" approved="yes">
+ <trans-unit id="_msg381" approved="yes">
<source xml:space="preserve">Your current spendable balance</source>
<target xml:space="preserve">Your current spendable balance</target>
<context-group purpose="location"><context context-type="linenumber">304</context></context-group>
</trans-unit>
- <trans-unit id="_msg374">
+ <trans-unit id="_msg382">
<source xml:space="preserve">Pending:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">339</context></context-group>
</trans-unit>
- <trans-unit id="_msg375" approved="yes">
+ <trans-unit id="_msg383" approved="yes">
<source xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
<target xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</target>
<context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
- <trans-unit id="_msg376" approved="yes">
+ <trans-unit id="_msg384" approved="yes">
<source xml:space="preserve">Immature:</source>
<target xml:space="preserve">Immature:</target>
<context-group purpose="location"><context context-type="linenumber">239</context></context-group>
</trans-unit>
- <trans-unit id="_msg377" approved="yes">
+ <trans-unit id="_msg385" approved="yes">
<source xml:space="preserve">Mined balance that has not yet matured</source>
<target xml:space="preserve">Mined balance that has not yet matured</target>
<context-group purpose="location"><context context-type="linenumber">210</context></context-group>
</trans-unit>
- <trans-unit id="_msg378">
+ <trans-unit id="_msg386">
<source xml:space="preserve">Balances</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">60</context></context-group>
</trans-unit>
- <trans-unit id="_msg379" approved="yes">
+ <trans-unit id="_msg387" approved="yes">
<source xml:space="preserve">Total:</source>
<target xml:space="preserve">Total:</target>
<context-group purpose="location"><context context-type="linenumber">200</context></context-group>
</trans-unit>
- <trans-unit id="_msg380" approved="yes">
+ <trans-unit id="_msg388" approved="yes">
<source xml:space="preserve">Your current total balance</source>
<target xml:space="preserve">Your current total balance</target>
<context-group purpose="location"><context context-type="linenumber">249</context></context-group>
</trans-unit>
- <trans-unit id="_msg381">
+ <trans-unit id="_msg389">
<source xml:space="preserve">Your current balance in watch-only addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">323</context></context-group>
</trans-unit>
- <trans-unit id="_msg382">
+ <trans-unit id="_msg390">
<source xml:space="preserve">Spendable:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">346</context></context-group>
</trans-unit>
- <trans-unit id="_msg383">
+ <trans-unit id="_msg391">
<source xml:space="preserve">Recent transactions</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">395</context></context-group>
</trans-unit>
- <trans-unit id="_msg384">
+ <trans-unit id="_msg392">
<source xml:space="preserve">Unconfirmed transactions to watch-only addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
</trans-unit>
- <trans-unit id="_msg385">
+ <trans-unit id="_msg393">
<source xml:space="preserve">Mined balance in watch-only addresses that has not yet matured</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">158</context></context-group>
</trans-unit>
- <trans-unit id="_msg386">
+ <trans-unit id="_msg394">
<source xml:space="preserve">Current total balance in watch-only addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">268</context></context-group>
@@ -2098,41 +2140,41 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../overviewpage.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="OverviewPage">
- <trans-unit id="_msg387">
+ <trans-unit id="_msg395">
<source xml:space="preserve">Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings-&gt;Mask values.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">193</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">188</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/psbtoperationsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog">
- <trans-unit id="_msg388">
+ <trans-unit id="_msg396">
<source xml:space="preserve">Dialog</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg389">
+ <trans-unit id="_msg397">
<source xml:space="preserve">Sign Tx</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">86</context></context-group>
</trans-unit>
- <trans-unit id="_msg390">
+ <trans-unit id="_msg398">
<source xml:space="preserve">Broadcast Tx</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">102</context></context-group>
</trans-unit>
- <trans-unit id="_msg391">
+ <trans-unit id="_msg399">
<source xml:space="preserve">Copy to Clipboard</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">122</context></context-group>
</trans-unit>
- <trans-unit id="_msg392">
+ <trans-unit id="_msg400">
<source xml:space="preserve">Save…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">129</context></context-group>
</trans-unit>
- <trans-unit id="_msg393">
+ <trans-unit id="_msg401">
<source xml:space="preserve">Close</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">136</context></context-group>
@@ -2141,123 +2183,123 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../psbtoperationsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog">
- <trans-unit id="_msg394">
+ <trans-unit id="_msg402">
<source xml:space="preserve">Failed to load transaction: %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">55</context></context-group>
</trans-unit>
- <trans-unit id="_msg395">
+ <trans-unit id="_msg403">
<source xml:space="preserve">Failed to sign transaction: %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">73</context></context-group>
</trans-unit>
- <trans-unit id="_msg396">
+ <trans-unit id="_msg404">
<source xml:space="preserve">Could not sign any more inputs.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">81</context></context-group>
</trans-unit>
- <trans-unit id="_msg397">
+ <trans-unit id="_msg405">
<source xml:space="preserve">Signed %1 inputs, but more signatures are still required.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">83</context></context-group>
</trans-unit>
- <trans-unit id="_msg398">
+ <trans-unit id="_msg406">
<source xml:space="preserve">Signed transaction successfully. Transaction is ready to broadcast.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">86</context></context-group>
</trans-unit>
- <trans-unit id="_msg399">
+ <trans-unit id="_msg407">
<source xml:space="preserve">Unknown error processing transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">98</context></context-group>
</trans-unit>
- <trans-unit id="_msg400">
+ <trans-unit id="_msg408">
<source xml:space="preserve">Transaction broadcast successfully! Transaction ID: %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">108</context></context-group>
</trans-unit>
- <trans-unit id="_msg401">
+ <trans-unit id="_msg409">
<source xml:space="preserve">Transaction broadcast failed: %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
- <trans-unit id="_msg402">
+ <trans-unit id="_msg410">
<source xml:space="preserve">PSBT copied to clipboard.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
</trans-unit>
- <trans-unit id="_msg403">
+ <trans-unit id="_msg411">
<source xml:space="preserve">Save Transaction Data</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">143</context></context-group>
</trans-unit>
- <trans-unit id="_msg404">
+ <trans-unit id="_msg412">
<source xml:space="preserve">Partially Signed Transaction (Binary)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">145</context></context-group>
<note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note>
</trans-unit>
- <trans-unit id="_msg405">
+ <trans-unit id="_msg413">
<source xml:space="preserve">PSBT saved to disk.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg406">
+ <trans-unit id="_msg414">
<source xml:space="preserve"> * Sends %1 to %2</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">168</context></context-group>
</trans-unit>
- <trans-unit id="_msg407">
+ <trans-unit id="_msg415">
<source xml:space="preserve">Unable to calculate transaction fee or total transaction amount.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">178</context></context-group>
</trans-unit>
- <trans-unit id="_msg408">
+ <trans-unit id="_msg416">
<source xml:space="preserve">Pays transaction fee: </source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">180</context></context-group>
</trans-unit>
- <trans-unit id="_msg409">
+ <trans-unit id="_msg417">
<source xml:space="preserve">Total Amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">192</context></context-group>
</trans-unit>
- <trans-unit id="_msg410">
+ <trans-unit id="_msg418">
<source xml:space="preserve">or</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">195</context></context-group>
</trans-unit>
- <trans-unit id="_msg411">
+ <trans-unit id="_msg419">
<source xml:space="preserve">Transaction has %1 unsigned inputs.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">201</context></context-group>
</trans-unit>
- <trans-unit id="_msg412">
+ <trans-unit id="_msg420">
<source xml:space="preserve">Transaction is missing some information about inputs.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">243</context></context-group>
</trans-unit>
- <trans-unit id="_msg413">
+ <trans-unit id="_msg421">
<source xml:space="preserve">Transaction still needs signature(s).</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">247</context></context-group>
</trans-unit>
- <trans-unit id="_msg414">
+ <trans-unit id="_msg422">
<source xml:space="preserve">(But this wallet cannot sign transactions.)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">250</context></context-group>
</trans-unit>
- <trans-unit id="_msg415">
+ <trans-unit id="_msg423">
<source xml:space="preserve">(But this wallet does not have the right keys.)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">253</context></context-group>
</trans-unit>
- <trans-unit id="_msg416">
+ <trans-unit id="_msg424">
<source xml:space="preserve">Transaction is fully signed and ready for broadcast.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">261</context></context-group>
</trans-unit>
- <trans-unit id="_msg417">
+ <trans-unit id="_msg425">
<source xml:space="preserve">Transaction status is unknown.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">265</context></context-group>
@@ -2266,17 +2308,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</body></file>
<file original="../paymentserver.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="PaymentServer">
- <trans-unit id="_msg418">
+ <trans-unit id="_msg426">
<source xml:space="preserve">Payment request error</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">173</context></context-group>
</trans-unit>
- <trans-unit id="_msg419">
+ <trans-unit id="_msg427">
<source xml:space="preserve">Cannot start bitcoin: click-to-pay handler</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">174</context></context-group>
</trans-unit>
- <trans-unit id="_msg420">
+ <trans-unit id="_msg428">
<source xml:space="preserve">URI handling</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">224</context></context-group>
@@ -2284,12 +2326,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context-group purpose="location"><context context-type="linenumber">246</context></context-group>
<context-group purpose="location"><context context-type="linenumber">253</context></context-group>
</trans-unit>
- <trans-unit id="_msg421">
+ <trans-unit id="_msg429">
<source xml:space="preserve">&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">224</context></context-group>
</trans-unit>
- <trans-unit id="_msg422">
+ <trans-unit id="_msg430">
<source xml:space="preserve">Cannot process payment request because BIP70 is not supported.
Due to widespread security flaws in BIP70 it&apos;s strongly recommended that any merchant instructions to switch wallets be ignored.
If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
@@ -2297,12 +2339,12 @@ If you are receiving this error you should request the merchant provide a BIP21
<context-group purpose="location"><context context-type="linenumber">241</context></context-group>
<context-group purpose="location"><context context-type="linenumber">264</context></context-group>
</trans-unit>
- <trans-unit id="_msg423">
+ <trans-unit id="_msg431">
<source xml:space="preserve">URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">254</context></context-group>
</trans-unit>
- <trans-unit id="_msg424">
+ <trans-unit id="_msg432">
<source xml:space="preserve">Payment request file handling</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">263</context></context-group>
@@ -2311,59 +2353,59 @@ If you are receiving this error you should request the merchant provide a BIP21
</body></file>
<file original="../peertablemodel.h" datatype="c" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="PeerTableModel">
- <trans-unit id="_msg425">
+ <trans-unit id="_msg433">
<source xml:space="preserve">User Agent</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">107</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which contains the peer&apos;s User Agent string.</note>
</trans-unit>
- <trans-unit id="_msg426">
+ <trans-unit id="_msg434">
<source xml:space="preserve">Ping</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">92</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which indicates the current latency of the connection with the peer.</note>
</trans-unit>
- <trans-unit id="_msg427">
+ <trans-unit id="_msg435">
<source xml:space="preserve">Peer</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which contains a unique number used to identify a connection.</note>
</trans-unit>
- <trans-unit id="_msg428">
+ <trans-unit id="_msg436">
<source xml:space="preserve">Sent</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</note>
</trans-unit>
- <trans-unit id="_msg429">
+ <trans-unit id="_msg437">
<source xml:space="preserve">Received</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">104</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have received from the peer.</note>
</trans-unit>
- <trans-unit id="_msg430">
+ <trans-unit id="_msg438">
<source xml:space="preserve">Address</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">83</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">89</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</note>
</trans-unit>
- <trans-unit id="_msg431">
+ <trans-unit id="_msg439">
<source xml:space="preserve">Type</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">92</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which describes the type of peer connection. The &quot;type&quot; describes why the connection exists.</note>
</trans-unit>
- <trans-unit id="_msg432">
+ <trans-unit id="_msg440">
<source xml:space="preserve">Network</source>
<target xml:space="preserve" state="needs-review-translation">Network</target>
- <context-group purpose="location"><context context-type="linenumber">89</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
<note annotates="source" from="developer">Title of Peers Table column which states the network the peer connected through.</note>
</trans-unit>
</group>
</body></file>
<file original="../bitcoinunits.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="QObject">
- <trans-unit id="_msg433">
+ <trans-unit id="_msg441">
<source xml:space="preserve">Amount</source>
<target xml:space="preserve" state="needs-review-translation">Amount</target>
<context-group purpose="location"><context context-type="linenumber">213</context></context-group>
@@ -2372,132 +2414,132 @@ If you are receiving this error you should request the merchant provide a BIP21
</body></file>
<file original="../guiutil.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="QObject">
- <trans-unit id="_msg434">
+ <trans-unit id="_msg442">
<source xml:space="preserve">Enter a Bitcoin address (e.g. %1)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
</trans-unit>
- <trans-unit id="_msg435">
+ <trans-unit id="_msg443">
<source xml:space="preserve">Unroutable</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">660</context></context-group>
</trans-unit>
- <trans-unit id="_msg436">
+ <trans-unit id="_msg444">
<source xml:space="preserve">Internal</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">666</context></context-group>
</trans-unit>
- <trans-unit id="_msg437">
+ <trans-unit id="_msg445">
<source xml:space="preserve">Inbound</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">676</context></context-group>
</trans-unit>
- <trans-unit id="_msg438">
+ <trans-unit id="_msg446">
<source xml:space="preserve">Outbound</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">676</context></context-group>
</trans-unit>
- <trans-unit id="_msg439">
+ <trans-unit id="_msg447">
<source xml:space="preserve">Full Relay</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">680</context></context-group>
</trans-unit>
- <trans-unit id="_msg440">
+ <trans-unit id="_msg448">
<source xml:space="preserve">Block Relay</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">681</context></context-group>
</trans-unit>
- <trans-unit id="_msg441">
+ <trans-unit id="_msg449">
<source xml:space="preserve">Manual</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">682</context></context-group>
</trans-unit>
- <trans-unit id="_msg442">
+ <trans-unit id="_msg450">
<source xml:space="preserve">Feeler</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">683</context></context-group>
</trans-unit>
- <trans-unit id="_msg443">
+ <trans-unit id="_msg451">
<source xml:space="preserve">Address Fetch</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">684</context></context-group>
</trans-unit>
- <trans-unit id="_msg444">
+ <trans-unit id="_msg452">
<source xml:space="preserve">%1 d</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">698</context></context-group>
</trans-unit>
- <trans-unit id="_msg445">
+ <trans-unit id="_msg453">
<source xml:space="preserve">%1 h</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">700</context></context-group>
</trans-unit>
- <trans-unit id="_msg446">
+ <trans-unit id="_msg454">
<source xml:space="preserve">%1 m</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">702</context></context-group>
</trans-unit>
- <trans-unit id="_msg447">
+ <trans-unit id="_msg455">
<source xml:space="preserve">%1 s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">704</context></context-group>
<context-group purpose="location"><context context-type="linenumber">732</context></context-group>
</trans-unit>
- <trans-unit id="_msg448">
+ <trans-unit id="_msg456">
<source xml:space="preserve">None</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">720</context></context-group>
</trans-unit>
- <trans-unit id="_msg449">
+ <trans-unit id="_msg457">
<source xml:space="preserve">N/A</source>
<target xml:space="preserve" state="needs-review-translation">N/A</target>
<context-group purpose="location"><context context-type="linenumber">726</context></context-group>
</trans-unit>
- <trans-unit id="_msg450">
+ <trans-unit id="_msg458">
<source xml:space="preserve">%1 ms</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">727</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">745</context></context-group>
- <trans-unit id="_msg451[0]" approved="yes">
+ <trans-unit id="_msg459[0]" approved="yes">
<source xml:space="preserve">%n second(s)</source>
<target xml:space="preserve">%n second</target>
</trans-unit>
- <trans-unit id="_msg451[1]" approved="yes">
+ <trans-unit id="_msg459[1]" approved="yes">
<source xml:space="preserve">%n second(s)</source>
<target xml:space="preserve">%n seconds</target>
</trans-unit>
</group>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">749</context></context-group>
- <trans-unit id="_msg452[0]" approved="yes">
+ <trans-unit id="_msg460[0]" approved="yes">
<source xml:space="preserve">%n minute(s)</source>
<target xml:space="preserve">%n minute</target>
</trans-unit>
- <trans-unit id="_msg452[1]" approved="yes">
+ <trans-unit id="_msg460[1]" approved="yes">
<source xml:space="preserve">%n minute(s)</source>
<target xml:space="preserve">%n minutes</target>
</trans-unit>
</group>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">753</context></context-group>
- <trans-unit id="_msg453[0]">
+ <trans-unit id="_msg461[0]">
<source xml:space="preserve">%n hour(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n hour</target>
</trans-unit>
- <trans-unit id="_msg453[1]">
+ <trans-unit id="_msg461[1]">
<source xml:space="preserve">%n hour(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n hours</target>
</trans-unit>
</group>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">757</context></context-group>
- <trans-unit id="_msg454[0]">
+ <trans-unit id="_msg462[0]">
<source xml:space="preserve">%n day(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n day</target>
</trans-unit>
- <trans-unit id="_msg454[1]">
+ <trans-unit id="_msg462[1]">
<source xml:space="preserve">%n day(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n days</target>
</trans-unit>
@@ -2505,47 +2547,47 @@ If you are receiving this error you should request the merchant provide a BIP21
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">761</context></context-group>
<context-group purpose="location"><context context-type="linenumber">767</context></context-group>
- <trans-unit id="_msg455[0]">
+ <trans-unit id="_msg463[0]">
<source xml:space="preserve">%n week(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n week</target>
</trans-unit>
- <trans-unit id="_msg455[1]">
+ <trans-unit id="_msg463[1]">
<source xml:space="preserve">%n week(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n weeks</target>
</trans-unit>
</group>
- <trans-unit id="_msg456">
+ <trans-unit id="_msg464">
<source xml:space="preserve">%1 and %2</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">767</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">767</context></context-group>
- <trans-unit id="_msg457[0]">
+ <trans-unit id="_msg465[0]">
<source xml:space="preserve">%n year(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n year</target>
</trans-unit>
- <trans-unit id="_msg457[1]">
+ <trans-unit id="_msg465[1]">
<source xml:space="preserve">%n year(s)</source>
<target xml:space="preserve" state="needs-review-translation">%n years</target>
</trans-unit>
</group>
- <trans-unit id="_msg458">
+ <trans-unit id="_msg466">
<source xml:space="preserve">%1 B</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">775</context></context-group>
</trans-unit>
- <trans-unit id="_msg459">
+ <trans-unit id="_msg467">
<source xml:space="preserve">%1 kB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">777</context></context-group>
</trans-unit>
- <trans-unit id="_msg460">
+ <trans-unit id="_msg468">
<source xml:space="preserve">%1 MB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">779</context></context-group>
</trans-unit>
- <trans-unit id="_msg461">
+ <trans-unit id="_msg469">
<source xml:space="preserve">%1 GB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">781</context></context-group>
@@ -2554,37 +2596,37 @@ If you are receiving this error you should request the merchant provide a BIP21
</body></file>
<file original="../qrimagewidget.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="QRImageWidget">
- <trans-unit id="_msg462">
- <source xml:space="preserve">Save Image…</source>
+ <trans-unit id="_msg470">
+ <source xml:space="preserve">&amp;Save Image…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">30</context></context-group>
</trans-unit>
- <trans-unit id="_msg463">
- <source xml:space="preserve">Copy Image</source>
+ <trans-unit id="_msg471">
+ <source xml:space="preserve">&amp;Copy Image</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">31</context></context-group>
</trans-unit>
- <trans-unit id="_msg464">
+ <trans-unit id="_msg472">
<source xml:space="preserve">Resulting URI too long, try to reduce the text for label / message.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">42</context></context-group>
</trans-unit>
- <trans-unit id="_msg465">
+ <trans-unit id="_msg473">
<source xml:space="preserve">Error encoding URI into QR Code.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
- <trans-unit id="_msg466">
+ <trans-unit id="_msg474">
<source xml:space="preserve">QR code support not available.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
- <trans-unit id="_msg467">
+ <trans-unit id="_msg475">
<source xml:space="preserve">Save QR Code</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
</trans-unit>
- <trans-unit id="_msg468">
+ <trans-unit id="_msg476">
<source xml:space="preserve">PNG Image</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">123</context></context-group>
@@ -2594,7 +2636,7 @@ If you are receiving this error you should request the merchant provide a BIP21
</body></file>
<file original="../forms/debugwindow.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="RPCConsole">
- <trans-unit id="_msg469" approved="yes">
+ <trans-unit id="_msg477" approved="yes">
<source xml:space="preserve">N/A</source>
<target xml:space="preserve">N/A</target>
<context-group purpose="location"><context context-type="linenumber">75</context></context-group>
@@ -2608,480 +2650,480 @@ If you are receiving this error you should request the merchant provide a BIP21
<context-group purpose="location"><context context-type="linenumber">300</context></context-group>
<context-group purpose="location"><context context-type="linenumber">336</context></context-group>
<context-group purpose="location"><context context-type="linenumber">359</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1081</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1107</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1133</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1156</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1179</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1202</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1231</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1257</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1280</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1303</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1326</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1349</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1375</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1401</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1424</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1447</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1470</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1493</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1516</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1542</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1565</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1588</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1614</context></context-group>
- <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.h</context><context context-type="linenumber">138</context></context-group>
- </trans-unit>
- <trans-unit id="_msg470" approved="yes">
+ <context-group purpose="location"><context context-type="linenumber">1051</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1077</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1103</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1126</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1149</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1172</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1201</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1227</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1250</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1273</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1296</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1319</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1345</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1371</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1394</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1417</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1440</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1463</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1486</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1512</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1535</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1558</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1584</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.h</context><context context-type="linenumber">139</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg478" approved="yes">
<source xml:space="preserve">Client version</source>
<target xml:space="preserve">Client version</target>
<context-group purpose="location"><context context-type="linenumber">65</context></context-group>
</trans-unit>
- <trans-unit id="_msg471" approved="yes">
+ <trans-unit id="_msg479" approved="yes">
<source xml:space="preserve">&amp;Information</source>
<target xml:space="preserve">&amp;Information</target>
<context-group purpose="location"><context context-type="linenumber">43</context></context-group>
</trans-unit>
- <trans-unit id="_msg472">
+ <trans-unit id="_msg480">
<source xml:space="preserve">General</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">58</context></context-group>
</trans-unit>
- <trans-unit id="_msg473">
+ <trans-unit id="_msg481">
<source xml:space="preserve">Datadir</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">114</context></context-group>
</trans-unit>
- <trans-unit id="_msg474">
+ <trans-unit id="_msg482">
<source xml:space="preserve">To specify a non-default location of the data directory use the &apos;%1&apos; option.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">124</context></context-group>
</trans-unit>
- <trans-unit id="_msg475">
+ <trans-unit id="_msg483">
<source xml:space="preserve">Blocksdir</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">143</context></context-group>
</trans-unit>
- <trans-unit id="_msg476">
+ <trans-unit id="_msg484">
<source xml:space="preserve">To specify a non-default location of the blocks directory use the &apos;%1&apos; option.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">153</context></context-group>
</trans-unit>
- <trans-unit id="_msg477" approved="yes">
+ <trans-unit id="_msg485" approved="yes">
<source xml:space="preserve">Startup time</source>
<target xml:space="preserve">Startup time</target>
<context-group purpose="location"><context context-type="linenumber">172</context></context-group>
</trans-unit>
- <trans-unit id="_msg478" approved="yes">
+ <trans-unit id="_msg486" approved="yes">
<source xml:space="preserve">Network</source>
<target xml:space="preserve">Network</target>
<context-group purpose="location"><context context-type="linenumber">201</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1123</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1093</context></context-group>
</trans-unit>
- <trans-unit id="_msg479">
+ <trans-unit id="_msg487">
<source xml:space="preserve">Name</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">208</context></context-group>
</trans-unit>
- <trans-unit id="_msg480" approved="yes">
+ <trans-unit id="_msg488" approved="yes">
<source xml:space="preserve">Number of connections</source>
<target xml:space="preserve">Number of connections</target>
<context-group purpose="location"><context context-type="linenumber">231</context></context-group>
</trans-unit>
- <trans-unit id="_msg481" approved="yes">
+ <trans-unit id="_msg489" approved="yes">
<source xml:space="preserve">Block chain</source>
<target xml:space="preserve">Block chain</target>
<context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
- <trans-unit id="_msg482">
+ <trans-unit id="_msg490">
<source xml:space="preserve">Memory Pool</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">319</context></context-group>
</trans-unit>
- <trans-unit id="_msg483">
+ <trans-unit id="_msg491">
<source xml:space="preserve">Current number of transactions</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">326</context></context-group>
</trans-unit>
- <trans-unit id="_msg484">
+ <trans-unit id="_msg492">
<source xml:space="preserve">Memory usage</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">349</context></context-group>
</trans-unit>
- <trans-unit id="_msg485">
+ <trans-unit id="_msg493">
<source xml:space="preserve">Wallet: </source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">443</context></context-group>
</trans-unit>
- <trans-unit id="_msg486">
+ <trans-unit id="_msg494">
<source xml:space="preserve">(none)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">454</context></context-group>
</trans-unit>
- <trans-unit id="_msg487">
+ <trans-unit id="_msg495">
<source xml:space="preserve">&amp;Reset</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">695</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">665</context></context-group>
</trans-unit>
- <trans-unit id="_msg488">
+ <trans-unit id="_msg496">
<source xml:space="preserve">Received</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">775</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1483</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">745</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1453</context></context-group>
</trans-unit>
- <trans-unit id="_msg489">
+ <trans-unit id="_msg497">
<source xml:space="preserve">Sent</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">855</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1460</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">825</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1430</context></context-group>
</trans-unit>
- <trans-unit id="_msg490">
+ <trans-unit id="_msg498">
<source xml:space="preserve">&amp;Peers</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">896</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">866</context></context-group>
</trans-unit>
- <trans-unit id="_msg491">
+ <trans-unit id="_msg499">
<source xml:space="preserve">Banned peers</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">972</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">942</context></context-group>
</trans-unit>
- <trans-unit id="_msg492">
+ <trans-unit id="_msg500">
<source xml:space="preserve">Select a peer to view detailed information.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1040</context></context-group>
- <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1091</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1010</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1124</context></context-group>
</trans-unit>
- <trans-unit id="_msg493">
+ <trans-unit id="_msg501">
<source xml:space="preserve">Version</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1146</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1116</context></context-group>
</trans-unit>
- <trans-unit id="_msg494">
+ <trans-unit id="_msg502">
<source xml:space="preserve">Starting Block</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1270</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1240</context></context-group>
</trans-unit>
- <trans-unit id="_msg495">
+ <trans-unit id="_msg503">
<source xml:space="preserve">Synced Headers</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1293</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1263</context></context-group>
</trans-unit>
- <trans-unit id="_msg496">
+ <trans-unit id="_msg504">
<source xml:space="preserve">Synced Blocks</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1316</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1286</context></context-group>
</trans-unit>
- <trans-unit id="_msg497">
+ <trans-unit id="_msg505">
<source xml:space="preserve">The mapped Autonomous System used for diversifying peer selection.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1601</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1571</context></context-group>
</trans-unit>
- <trans-unit id="_msg498">
+ <trans-unit id="_msg506">
<source xml:space="preserve">Mapped AS</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1604</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1574</context></context-group>
</trans-unit>
- <trans-unit id="_msg499">
+ <trans-unit id="_msg507">
<source xml:space="preserve">User Agent</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">88</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">1169</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1139</context></context-group>
</trans-unit>
- <trans-unit id="_msg500">
+ <trans-unit id="_msg508">
<source xml:space="preserve">Node window</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg501">
+ <trans-unit id="_msg509">
<source xml:space="preserve">Current block height</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">267</context></context-group>
</trans-unit>
- <trans-unit id="_msg502">
+ <trans-unit id="_msg510">
<source xml:space="preserve">Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">397</context></context-group>
</trans-unit>
- <trans-unit id="_msg503">
+ <trans-unit id="_msg511">
<source xml:space="preserve">Decrease font size</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">481</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">475</context></context-group>
</trans-unit>
- <trans-unit id="_msg504">
+ <trans-unit id="_msg512">
<source xml:space="preserve">Increase font size</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">513</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">495</context></context-group>
</trans-unit>
- <trans-unit id="_msg505">
+ <trans-unit id="_msg513">
<source xml:space="preserve">Permissions</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1071</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1041</context></context-group>
</trans-unit>
- <trans-unit id="_msg506">
+ <trans-unit id="_msg514">
<source xml:space="preserve">The direction and type of peer connection: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1094</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1064</context></context-group>
</trans-unit>
- <trans-unit id="_msg507">
+ <trans-unit id="_msg515">
<source xml:space="preserve">Direction/Type</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1097</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1067</context></context-group>
</trans-unit>
- <trans-unit id="_msg508">
+ <trans-unit id="_msg516">
<source xml:space="preserve">The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1120</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1090</context></context-group>
</trans-unit>
- <trans-unit id="_msg509">
+ <trans-unit id="_msg517">
<source xml:space="preserve">Services</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1192</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1162</context></context-group>
</trans-unit>
- <trans-unit id="_msg510">
+ <trans-unit id="_msg518">
<source xml:space="preserve">Whether the peer requested us to relay transactions.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1218</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1188</context></context-group>
</trans-unit>
- <trans-unit id="_msg511">
+ <trans-unit id="_msg519">
<source xml:space="preserve">Wants Tx Relay</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1221</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1191</context></context-group>
</trans-unit>
- <trans-unit id="_msg512">
+ <trans-unit id="_msg520">
<source xml:space="preserve">High bandwidth BIP152 compact block relay: %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1244</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1214</context></context-group>
</trans-unit>
- <trans-unit id="_msg513">
+ <trans-unit id="_msg521">
<source xml:space="preserve">High Bandwidth</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1247</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1217</context></context-group>
</trans-unit>
- <trans-unit id="_msg514">
+ <trans-unit id="_msg522">
<source xml:space="preserve">Connection Time</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1339</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1309</context></context-group>
</trans-unit>
- <trans-unit id="_msg515">
+ <trans-unit id="_msg523">
<source xml:space="preserve">Elapsed time since a novel block passing initial validity checks was received from this peer.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1362</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1332</context></context-group>
</trans-unit>
- <trans-unit id="_msg516">
+ <trans-unit id="_msg524">
<source xml:space="preserve">Last Block</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1365</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1335</context></context-group>
</trans-unit>
- <trans-unit id="_msg517">
+ <trans-unit id="_msg525">
<source xml:space="preserve">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1388</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1358</context></context-group>
</trans-unit>
- <trans-unit id="_msg518">
+ <trans-unit id="_msg526">
<source xml:space="preserve">Last Tx</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1391</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1361</context></context-group>
</trans-unit>
- <trans-unit id="_msg519">
+ <trans-unit id="_msg527">
<source xml:space="preserve">Last Send</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1414</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1384</context></context-group>
</trans-unit>
- <trans-unit id="_msg520">
+ <trans-unit id="_msg528">
<source xml:space="preserve">Last Receive</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1437</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1407</context></context-group>
</trans-unit>
- <trans-unit id="_msg521">
+ <trans-unit id="_msg529">
<source xml:space="preserve">Ping Time</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1506</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1476</context></context-group>
</trans-unit>
- <trans-unit id="_msg522">
+ <trans-unit id="_msg530">
<source xml:space="preserve">The duration of a currently outstanding ping.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1529</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1499</context></context-group>
</trans-unit>
- <trans-unit id="_msg523">
+ <trans-unit id="_msg531">
<source xml:space="preserve">Ping Wait</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1532</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1502</context></context-group>
</trans-unit>
- <trans-unit id="_msg524">
+ <trans-unit id="_msg532">
<source xml:space="preserve">Min Ping</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1555</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1525</context></context-group>
</trans-unit>
- <trans-unit id="_msg525">
+ <trans-unit id="_msg533">
<source xml:space="preserve">Time Offset</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1578</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1548</context></context-group>
</trans-unit>
- <trans-unit id="_msg526" approved="yes">
+ <trans-unit id="_msg534" approved="yes">
<source xml:space="preserve">Last block time</source>
<target xml:space="preserve">Last block time</target>
<context-group purpose="location"><context context-type="linenumber">290</context></context-group>
</trans-unit>
- <trans-unit id="_msg527" approved="yes">
+ <trans-unit id="_msg535" approved="yes">
<source xml:space="preserve">&amp;Open</source>
<target xml:space="preserve">&amp;Open</target>
<context-group purpose="location"><context context-type="linenumber">400</context></context-group>
</trans-unit>
- <trans-unit id="_msg528" approved="yes">
+ <trans-unit id="_msg536" approved="yes">
<source xml:space="preserve">&amp;Console</source>
<target xml:space="preserve">&amp;Console</target>
<context-group purpose="location"><context context-type="linenumber">426</context></context-group>
</trans-unit>
- <trans-unit id="_msg529">
+ <trans-unit id="_msg537">
<source xml:space="preserve">&amp;Network Traffic</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">643</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">613</context></context-group>
</trans-unit>
- <trans-unit id="_msg530">
+ <trans-unit id="_msg538">
<source xml:space="preserve">Totals</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">711</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">681</context></context-group>
</trans-unit>
- <trans-unit id="_msg531" approved="yes">
+ <trans-unit id="_msg539" approved="yes">
<source xml:space="preserve">Debug log file</source>
<target xml:space="preserve">Debug log file</target>
<context-group purpose="location"><context context-type="linenumber">390</context></context-group>
</trans-unit>
- <trans-unit id="_msg532" approved="yes">
+ <trans-unit id="_msg540" approved="yes">
<source xml:space="preserve">Clear console</source>
<target xml:space="preserve">Clear console</target>
- <context-group purpose="location"><context context-type="linenumber">545</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">515</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../rpcconsole.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="RPCConsole">
- <trans-unit id="_msg533">
+ <trans-unit id="_msg541">
<source xml:space="preserve">In:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">890</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">923</context></context-group>
</trans-unit>
- <trans-unit id="_msg534">
+ <trans-unit id="_msg542">
<source xml:space="preserve">Out:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">891</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">924</context></context-group>
</trans-unit>
- <trans-unit id="_msg535">
+ <trans-unit id="_msg543">
<source xml:space="preserve">Inbound: initiated by peer</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">474</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">495</context></context-group>
</trans-unit>
- <trans-unit id="_msg536">
+ <trans-unit id="_msg544">
<source xml:space="preserve">Outbound Full Relay: default</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">475</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">496</context></context-group>
</trans-unit>
- <trans-unit id="_msg537">
+ <trans-unit id="_msg545">
<source xml:space="preserve">Outbound Block Relay: does not relay transactions or addresses</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">476</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">497</context></context-group>
</trans-unit>
- <trans-unit id="_msg538">
+ <trans-unit id="_msg546">
<source xml:space="preserve">Outbound Manual: added using RPC %1 or %2/%3 configuration options</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">477</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">498</context></context-group>
</trans-unit>
- <trans-unit id="_msg539">
+ <trans-unit id="_msg547">
<source xml:space="preserve">Outbound Feeler: short-lived, for testing addresses</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">481</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">502</context></context-group>
</trans-unit>
- <trans-unit id="_msg540">
+ <trans-unit id="_msg548">
<source xml:space="preserve">Outbound Address Fetch: short-lived, for soliciting addresses</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">482</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">503</context></context-group>
</trans-unit>
- <trans-unit id="_msg541">
+ <trans-unit id="_msg549">
<source xml:space="preserve">we selected the peer for high bandwidth relay</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">486</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">507</context></context-group>
</trans-unit>
- <trans-unit id="_msg542">
+ <trans-unit id="_msg550">
<source xml:space="preserve">the peer selected us for high bandwidth relay</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">487</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">508</context></context-group>
</trans-unit>
- <trans-unit id="_msg543">
+ <trans-unit id="_msg551">
<source xml:space="preserve">no high bandwidth relay selected</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">488</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
</trans-unit>
- <trans-unit id="_msg544">
+ <trans-unit id="_msg552">
<source xml:space="preserve">Ctrl++</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">501</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">522</context></context-group>
<note annotates="source" from="developer">Main shortcut to increase the RPC console font size.</note>
</trans-unit>
- <trans-unit id="_msg545">
+ <trans-unit id="_msg553">
<source xml:space="preserve">Ctrl+=</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">503</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">524</context></context-group>
<note annotates="source" from="developer">Secondary shortcut to increase the RPC console font size.</note>
</trans-unit>
- <trans-unit id="_msg546">
+ <trans-unit id="_msg554">
<source xml:space="preserve">Ctrl+-</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">507</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">528</context></context-group>
<note annotates="source" from="developer">Main shortcut to decrease the RPC console font size.</note>
</trans-unit>
- <trans-unit id="_msg547">
+ <trans-unit id="_msg555">
<source xml:space="preserve">Ctrl+_</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">530</context></context-group>
<note annotates="source" from="developer">Secondary shortcut to decrease the RPC console font size.</note>
</trans-unit>
- <trans-unit id="_msg548">
- <source xml:space="preserve">Network activity disabled</source>
+ <trans-unit id="_msg556">
+ <source xml:space="preserve">&amp;Disconnect</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">894</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">680</context></context-group>
</trans-unit>
- <trans-unit id="_msg549">
- <source xml:space="preserve">Executing command without any wallet</source>
+ <trans-unit id="_msg557">
+ <source xml:space="preserve">1 &amp;hour</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">971</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">681</context></context-group>
</trans-unit>
- <trans-unit id="_msg550">
- <source xml:space="preserve">Executing command using &quot;%1&quot; wallet</source>
+ <trans-unit id="_msg558">
+ <source xml:space="preserve">1 d&amp;ay</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">969</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">682</context></context-group>
</trans-unit>
- <trans-unit id="_msg551">
- <source xml:space="preserve">Disconnect</source>
+ <trans-unit id="_msg559">
+ <source xml:space="preserve">1 &amp;week</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">650</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">683</context></context-group>
</trans-unit>
- <trans-unit id="_msg552">
- <source xml:space="preserve">1 hour</source>
+ <trans-unit id="_msg560">
+ <source xml:space="preserve">1 &amp;year</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">651</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">684</context></context-group>
</trans-unit>
- <trans-unit id="_msg553">
- <source xml:space="preserve">1 day</source>
+ <trans-unit id="_msg561">
+ <source xml:space="preserve">&amp;Unban</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">652</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">706</context></context-group>
</trans-unit>
- <trans-unit id="_msg554">
- <source xml:space="preserve">1 week</source>
+ <trans-unit id="_msg562">
+ <source xml:space="preserve">Network activity disabled</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">653</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">927</context></context-group>
</trans-unit>
- <trans-unit id="_msg555">
- <source xml:space="preserve">1 year</source>
+ <trans-unit id="_msg563">
+ <source xml:space="preserve">Executing command without any wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">654</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1004</context></context-group>
</trans-unit>
- <trans-unit id="_msg556">
- <source xml:space="preserve">Unban</source>
+ <trans-unit id="_msg564">
+ <source xml:space="preserve">Executing command using &quot;%1&quot; wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">673</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1002</context></context-group>
</trans-unit>
- <trans-unit id="_msg557">
+ <trans-unit id="_msg565">
<source xml:space="preserve">Welcome to the %1 RPC console.
Use up and down arrows to navigate history, and %2 to clear screen.
Use %3 and %4 to increase or decrease the font size.
@@ -3090,160 +3132,160 @@ For more information on using this console, type %6.
%7WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.%8</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">823</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">856</context></context-group>
<note annotates="source" from="developer">RPC console welcome message. Placeholders %7 and %8 are style tags for the warning content, and they are not space separated from the rest of the text intentionally.</note>
</trans-unit>
- <trans-unit id="_msg558">
+ <trans-unit id="_msg566">
<source xml:space="preserve">Executing…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">979</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1012</context></context-group>
<note annotates="source" from="developer">A console message indicating an entered command is currently being executed.</note>
</trans-unit>
- <trans-unit id="_msg559">
+ <trans-unit id="_msg567">
<source xml:space="preserve">(peer: %1)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1097</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1130</context></context-group>
</trans-unit>
- <trans-unit id="_msg560">
+ <trans-unit id="_msg568">
<source xml:space="preserve">via %1</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">1099</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1132</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../rpcconsole.h" datatype="c" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="RPCConsole">
- <trans-unit id="_msg561">
+ <trans-unit id="_msg569">
<source xml:space="preserve">Yes</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg562">
+ <trans-unit id="_msg570">
<source xml:space="preserve">No</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg563">
+ <trans-unit id="_msg571">
<source xml:space="preserve">To</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg564">
+ <trans-unit id="_msg572">
<source xml:space="preserve">From</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg565">
+ <trans-unit id="_msg573">
<source xml:space="preserve">Ban for</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
- <trans-unit id="_msg566">
+ <trans-unit id="_msg574">
<source xml:space="preserve">Never</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">179</context></context-group>
</trans-unit>
- <trans-unit id="_msg567">
+ <trans-unit id="_msg575">
<source xml:space="preserve">Unknown</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/receivecoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog">
- <trans-unit id="_msg568">
+ <trans-unit id="_msg576">
<source xml:space="preserve">&amp;Amount:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">37</context></context-group>
</trans-unit>
- <trans-unit id="_msg569">
+ <trans-unit id="_msg577">
<source xml:space="preserve">&amp;Label:</source>
<target xml:space="preserve" state="needs-review-translation">&amp;Label:</target>
<context-group purpose="location"><context context-type="linenumber">83</context></context-group>
</trans-unit>
- <trans-unit id="_msg570">
+ <trans-unit id="_msg578">
<source xml:space="preserve">&amp;Message:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">53</context></context-group>
</trans-unit>
- <trans-unit id="_msg571">
+ <trans-unit id="_msg579">
<source xml:space="preserve">An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">50</context></context-group>
</trans-unit>
- <trans-unit id="_msg572">
+ <trans-unit id="_msg580">
<source xml:space="preserve">An optional label to associate with the new receiving address.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">80</context></context-group>
</trans-unit>
- <trans-unit id="_msg573">
+ <trans-unit id="_msg581">
<source xml:space="preserve">Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">73</context></context-group>
</trans-unit>
- <trans-unit id="_msg574">
+ <trans-unit id="_msg582">
<source xml:space="preserve">An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">34</context></context-group>
<context-group purpose="location"><context context-type="linenumber">193</context></context-group>
</trans-unit>
- <trans-unit id="_msg575">
+ <trans-unit id="_msg583">
<source xml:space="preserve">An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">66</context></context-group>
</trans-unit>
- <trans-unit id="_msg576">
+ <trans-unit id="_msg584">
<source xml:space="preserve">An optional message that is attached to the payment request and may be displayed to the sender.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">96</context></context-group>
</trans-unit>
- <trans-unit id="_msg577">
+ <trans-unit id="_msg585">
<source xml:space="preserve">&amp;Create new receiving address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
- <trans-unit id="_msg578">
+ <trans-unit id="_msg586">
<source xml:space="preserve">Clear all fields of the form.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">134</context></context-group>
</trans-unit>
- <trans-unit id="_msg579">
+ <trans-unit id="_msg587">
<source xml:space="preserve">Clear</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">137</context></context-group>
</trans-unit>
- <trans-unit id="_msg580">
+ <trans-unit id="_msg588">
<source xml:space="preserve">Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don&apos;t support them. When unchecked, an address compatible with older wallets will be created instead.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg581">
+ <trans-unit id="_msg589">
<source xml:space="preserve">Generate native segwit (Bech32) address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">218</context></context-group>
</trans-unit>
- <trans-unit id="_msg582">
+ <trans-unit id="_msg590">
<source xml:space="preserve">Requested payments history</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">279</context></context-group>
</trans-unit>
- <trans-unit id="_msg583">
+ <trans-unit id="_msg591">
<source xml:space="preserve">Show the selected request (does the same as double clicking an entry)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">304</context></context-group>
</trans-unit>
- <trans-unit id="_msg584">
+ <trans-unit id="_msg592">
<source xml:space="preserve">Show</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">307</context></context-group>
</trans-unit>
- <trans-unit id="_msg585">
+ <trans-unit id="_msg593">
<source xml:space="preserve">Remove the selected entries from the list</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">324</context></context-group>
</trans-unit>
- <trans-unit id="_msg586">
+ <trans-unit id="_msg594">
<source xml:space="preserve">Remove</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">327</context></context-group>
@@ -3252,37 +3294,37 @@ For more information on using this console, type %6.
</body></file>
<file original="../receivecoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog">
- <trans-unit id="_msg587">
- <source xml:space="preserve">Copy URI</source>
+ <trans-unit id="_msg595">
+ <source xml:space="preserve">Copy &amp;URI</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">47</context></context-group>
</trans-unit>
- <trans-unit id="_msg588">
- <source xml:space="preserve">Copy address</source>
+ <trans-unit id="_msg596">
+ <source xml:space="preserve">&amp;Copy address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">48</context></context-group>
</trans-unit>
- <trans-unit id="_msg589">
- <source xml:space="preserve">Copy label</source>
+ <trans-unit id="_msg597">
+ <source xml:space="preserve">Copy &amp;label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
- <trans-unit id="_msg590">
- <source xml:space="preserve">Copy message</source>
+ <trans-unit id="_msg598">
+ <source xml:space="preserve">Copy &amp;message</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">50</context></context-group>
</trans-unit>
- <trans-unit id="_msg591">
- <source xml:space="preserve">Copy amount</source>
+ <trans-unit id="_msg599">
+ <source xml:space="preserve">Copy &amp;amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">51</context></context-group>
</trans-unit>
- <trans-unit id="_msg592">
+ <trans-unit id="_msg600">
<source xml:space="preserve">Could not unlock wallet.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">176</context></context-group>
</trans-unit>
- <trans-unit id="_msg593">
+ <trans-unit id="_msg601">
<source xml:space="preserve">Could not generate new %1 address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">181</context></context-group>
@@ -3291,52 +3333,62 @@ For more information on using this console, type %6.
</body></file>
<file original="../forms/receiverequestdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog">
- <trans-unit id="_msg594">
+ <trans-unit id="_msg602">
<source xml:space="preserve">Request payment to …</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg595">
+ <trans-unit id="_msg603">
<source xml:space="preserve">Address:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
- <trans-unit id="_msg596">
+ <trans-unit id="_msg604">
<source xml:space="preserve">Amount:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">119</context></context-group>
</trans-unit>
- <trans-unit id="_msg597">
+ <trans-unit id="_msg605">
<source xml:space="preserve">Label:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">148</context></context-group>
</trans-unit>
- <trans-unit id="_msg598">
+ <trans-unit id="_msg606">
<source xml:space="preserve">Message:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">180</context></context-group>
</trans-unit>
- <trans-unit id="_msg599">
+ <trans-unit id="_msg607">
<source xml:space="preserve">Wallet:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">212</context></context-group>
</trans-unit>
- <trans-unit id="_msg600">
+ <trans-unit id="_msg608">
<source xml:space="preserve">Copy &amp;URI</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">240</context></context-group>
</trans-unit>
- <trans-unit id="_msg601">
+ <trans-unit id="_msg609">
<source xml:space="preserve">Copy &amp;Address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">250</context></context-group>
</trans-unit>
- <trans-unit id="_msg602">
- <source xml:space="preserve">&amp;Save Image…</source>
+ <trans-unit id="_msg610">
+ <source xml:space="preserve">&amp;Verify</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
- <trans-unit id="_msg603">
+ <trans-unit id="_msg611">
+ <source xml:space="preserve">Verify this address on e.g. a hardware wallet screen</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg612">
+ <source xml:space="preserve">&amp;Save Image…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">273</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg613">
<source xml:space="preserve">Payment information</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">39</context></context-group>
@@ -3345,7 +3397,7 @@ For more information on using this console, type %6.
</body></file>
<file original="../receiverequestdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog">
- <trans-unit id="_msg604">
+ <trans-unit id="_msg614">
<source xml:space="preserve">Request payment to %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
@@ -3354,229 +3406,229 @@ For more information on using this console, type %6.
</body></file>
<file original="../recentrequeststablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="RecentRequestsTableModel">
- <trans-unit id="_msg605">
+ <trans-unit id="_msg615">
<source xml:space="preserve">Date</source>
<target xml:space="preserve" state="needs-review-translation">Date</target>
- <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">32</context></context-group>
</trans-unit>
- <trans-unit id="_msg606">
+ <trans-unit id="_msg616">
<source xml:space="preserve">Label</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">32</context></context-group>
</trans-unit>
- <trans-unit id="_msg607">
+ <trans-unit id="_msg617">
<source xml:space="preserve">Message</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">32</context></context-group>
</trans-unit>
- <trans-unit id="_msg608">
+ <trans-unit id="_msg618">
<source xml:space="preserve">(no label)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">71</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
</trans-unit>
- <trans-unit id="_msg609">
+ <trans-unit id="_msg619">
<source xml:space="preserve">(no message)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">82</context></context-group>
</trans-unit>
- <trans-unit id="_msg610">
+ <trans-unit id="_msg620">
<source xml:space="preserve">(no amount requested)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
- <trans-unit id="_msg611">
+ <trans-unit id="_msg621">
<source xml:space="preserve">Requested</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">133</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/sendcoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="SendCoinsDialog">
- <trans-unit id="_msg612" approved="yes">
+ <trans-unit id="_msg622" approved="yes">
<source xml:space="preserve">Send Coins</source>
<target xml:space="preserve">Send Coins</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
- <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">674</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">738</context></context-group>
</trans-unit>
- <trans-unit id="_msg613">
+ <trans-unit id="_msg623">
<source xml:space="preserve">Coin Control Features</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
- <trans-unit id="_msg614">
+ <trans-unit id="_msg624">
<source xml:space="preserve">automatically selected</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
</trans-unit>
- <trans-unit id="_msg615">
+ <trans-unit id="_msg625">
<source xml:space="preserve">Insufficient funds!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
- <trans-unit id="_msg616">
+ <trans-unit id="_msg626">
<source xml:space="preserve">Quantity:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">228</context></context-group>
</trans-unit>
- <trans-unit id="_msg617">
+ <trans-unit id="_msg627">
<source xml:space="preserve">Bytes:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">263</context></context-group>
</trans-unit>
- <trans-unit id="_msg618">
+ <trans-unit id="_msg628">
<source xml:space="preserve">Amount:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">311</context></context-group>
</trans-unit>
- <trans-unit id="_msg619">
+ <trans-unit id="_msg629">
<source xml:space="preserve">Fee:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">391</context></context-group>
</trans-unit>
- <trans-unit id="_msg620">
+ <trans-unit id="_msg630">
<source xml:space="preserve">After Fee:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">442</context></context-group>
</trans-unit>
- <trans-unit id="_msg621">
+ <trans-unit id="_msg631">
<source xml:space="preserve">Change:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">474</context></context-group>
</trans-unit>
- <trans-unit id="_msg622">
+ <trans-unit id="_msg632">
<source xml:space="preserve">If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">518</context></context-group>
</trans-unit>
- <trans-unit id="_msg623">
+ <trans-unit id="_msg633">
<source xml:space="preserve">Custom change address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">521</context></context-group>
</trans-unit>
- <trans-unit id="_msg624">
+ <trans-unit id="_msg634">
<source xml:space="preserve">Transaction Fee:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">727</context></context-group>
</trans-unit>
- <trans-unit id="_msg625">
+ <trans-unit id="_msg635">
<source xml:space="preserve">Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">765</context></context-group>
</trans-unit>
- <trans-unit id="_msg626">
+ <trans-unit id="_msg636">
<source xml:space="preserve">Warning: Fee estimation is currently not possible.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">774</context></context-group>
</trans-unit>
- <trans-unit id="_msg627">
+ <trans-unit id="_msg637">
<source xml:space="preserve">per kilobyte</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">856</context></context-group>
</trans-unit>
- <trans-unit id="_msg628">
+ <trans-unit id="_msg638">
<source xml:space="preserve">Hide</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">803</context></context-group>
</trans-unit>
- <trans-unit id="_msg629">
+ <trans-unit id="_msg639">
<source xml:space="preserve">Recommended:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">915</context></context-group>
</trans-unit>
- <trans-unit id="_msg630">
+ <trans-unit id="_msg640">
<source xml:space="preserve">Custom:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">945</context></context-group>
</trans-unit>
- <trans-unit id="_msg631" approved="yes">
+ <trans-unit id="_msg641" approved="yes">
<source xml:space="preserve">Send to multiple recipients at once</source>
<target xml:space="preserve">Send to multiple recipients at once</target>
<context-group purpose="location"><context context-type="linenumber">1160</context></context-group>
</trans-unit>
- <trans-unit id="_msg632" approved="yes">
+ <trans-unit id="_msg642" approved="yes">
<source xml:space="preserve">Add &amp;Recipient</source>
<target xml:space="preserve">Add &amp;Recipient</target>
<context-group purpose="location"><context context-type="linenumber">1163</context></context-group>
</trans-unit>
- <trans-unit id="_msg633">
+ <trans-unit id="_msg643">
<source xml:space="preserve">Clear all fields of the form.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">1143</context></context-group>
</trans-unit>
- <trans-unit id="_msg634">
+ <trans-unit id="_msg644">
<source xml:space="preserve">Inputs…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">110</context></context-group>
</trans-unit>
- <trans-unit id="_msg635">
+ <trans-unit id="_msg645">
<source xml:space="preserve">Dust:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">343</context></context-group>
</trans-unit>
- <trans-unit id="_msg636">
+ <trans-unit id="_msg646">
<source xml:space="preserve">Choose…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">741</context></context-group>
</trans-unit>
- <trans-unit id="_msg637">
+ <trans-unit id="_msg647">
<source xml:space="preserve">Hide transaction fee settings</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">800</context></context-group>
</trans-unit>
- <trans-unit id="_msg638">
+ <trans-unit id="_msg648">
<source xml:space="preserve">Specify a custom fee per kB (1,000 bytes) of the transaction&apos;s virtual size.
Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100 satoshis per kvB&quot; for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">851</context></context-group>
</trans-unit>
- <trans-unit id="_msg639">
+ <trans-unit id="_msg649">
<source xml:space="preserve">When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">886</context></context-group>
</trans-unit>
- <trans-unit id="_msg640">
+ <trans-unit id="_msg650">
<source xml:space="preserve">A too low fee might result in a never confirming transaction (read the tooltip)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">889</context></context-group>
</trans-unit>
- <trans-unit id="_msg641">
+ <trans-unit id="_msg651">
<source xml:space="preserve">(Smart fee not initialized yet. This usually takes a few blocks…)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">994</context></context-group>
</trans-unit>
- <trans-unit id="_msg642">
+ <trans-unit id="_msg652">
<source xml:space="preserve">Confirmation time target:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">1020</context></context-group>
</trans-unit>
- <trans-unit id="_msg643">
+ <trans-unit id="_msg653">
<source xml:space="preserve">Enable Replace-By-Fee</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">1078</context></context-group>
</trans-unit>
- <trans-unit id="_msg644">
+ <trans-unit id="_msg654">
<source xml:space="preserve">With Replace-By-Fee (BIP-125) you can increase a transaction&apos;s fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">1081</context></context-group>
</trans-unit>
- <trans-unit id="_msg645" approved="yes">
+ <trans-unit id="_msg655" approved="yes">
<source xml:space="preserve">Clear &amp;All</source>
<target xml:space="preserve">Clear &amp;All</target>
<context-group purpose="location"><context context-type="linenumber">1146</context></context-group>
</trans-unit>
- <trans-unit id="_msg646" approved="yes">
+ <trans-unit id="_msg656" approved="yes">
<source xml:space="preserve">Balance:</source>
<target xml:space="preserve">Balance:</target>
<context-group purpose="location"><context context-type="linenumber">1201</context></context-group>
</trans-unit>
- <trans-unit id="_msg647" approved="yes">
+ <trans-unit id="_msg657" approved="yes">
<source xml:space="preserve">Confirm the send action</source>
<target xml:space="preserve">Confirm the send action</target>
<context-group purpose="location"><context context-type="linenumber">1117</context></context-group>
</trans-unit>
- <trans-unit id="_msg648" approved="yes">
+ <trans-unit id="_msg658" approved="yes">
<source xml:space="preserve">S&amp;end</source>
<target xml:space="preserve">S&amp;end</target>
<context-group purpose="location"><context context-type="linenumber">1120</context></context-group>
@@ -3585,344 +3637,383 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../sendcoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="SendCoinsDialog">
- <trans-unit id="_msg649">
+ <trans-unit id="_msg659">
<source xml:space="preserve">Copy quantity</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">92</context></context-group>
</trans-unit>
- <trans-unit id="_msg650">
+ <trans-unit id="_msg660">
<source xml:space="preserve">Copy amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">93</context></context-group>
</trans-unit>
- <trans-unit id="_msg651">
+ <trans-unit id="_msg661">
<source xml:space="preserve">Copy fee</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">94</context></context-group>
</trans-unit>
- <trans-unit id="_msg652">
+ <trans-unit id="_msg662">
<source xml:space="preserve">Copy after fee</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">95</context></context-group>
</trans-unit>
- <trans-unit id="_msg653">
+ <trans-unit id="_msg663">
<source xml:space="preserve">Copy bytes</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">96</context></context-group>
</trans-unit>
- <trans-unit id="_msg654">
+ <trans-unit id="_msg664">
<source xml:space="preserve">Copy dust</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">97</context></context-group>
</trans-unit>
- <trans-unit id="_msg655">
+ <trans-unit id="_msg665">
<source xml:space="preserve">Copy change</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">98</context></context-group>
</trans-unit>
- <trans-unit id="_msg656">
+ <trans-unit id="_msg666">
<source xml:space="preserve">%1 (%2 blocks)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">174</context></context-group>
</trans-unit>
- <trans-unit id="_msg657">
+ <trans-unit id="_msg667">
+ <source xml:space="preserve">Sign on device</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">204</context></context-group>
+ <note annotates="source" from="developer">&quot;device&quot; usually means a hardware wallet</note>
+ </trans-unit>
+ <trans-unit id="_msg668">
+ <source xml:space="preserve">Connect your hardware wallet first.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">207</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg669">
+ <source xml:space="preserve">Set external signer script path in Options -&gt; Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">211</context></context-group>
+ <note annotates="source" from="developer">&quot;External signer&quot; means using devices such as hardware wallets.</note>
+ </trans-unit>
+ <trans-unit id="_msg670">
<source xml:space="preserve">Cr&amp;eate Unsigned</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">214</context></context-group>
</trans-unit>
- <trans-unit id="_msg658">
+ <trans-unit id="_msg671">
<source xml:space="preserve">Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">204</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg659">
+ <trans-unit id="_msg672">
<source xml:space="preserve"> from wallet &apos;%1&apos;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">294</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
- <trans-unit id="_msg660">
+ <trans-unit id="_msg673">
<source xml:space="preserve">%1 to &apos;%2&apos;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">316</context></context-group>
</trans-unit>
- <trans-unit id="_msg661">
+ <trans-unit id="_msg674">
<source xml:space="preserve">%1 to %2</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">310</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">321</context></context-group>
</trans-unit>
- <trans-unit id="_msg662">
+ <trans-unit id="_msg675">
<source xml:space="preserve">Do you want to draft this transaction?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">317</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">328</context></context-group>
</trans-unit>
- <trans-unit id="_msg663">
+ <trans-unit id="_msg676">
<source xml:space="preserve">Are you sure you want to send?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">319</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">330</context></context-group>
</trans-unit>
- <trans-unit id="_msg664">
+ <trans-unit id="_msg677">
<source xml:space="preserve">To review recipient list click &quot;Show Details…&quot;</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">371</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">382</context></context-group>
</trans-unit>
- <trans-unit id="_msg665">
+ <trans-unit id="_msg678">
<source xml:space="preserve">Create Unsigned</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">401</context></context-group>
</trans-unit>
- <trans-unit id="_msg666">
+ <trans-unit id="_msg679">
+ <source xml:space="preserve">Sign and send</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">401</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg680">
+ <source xml:space="preserve">Sign failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">426</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg681">
+ <source xml:space="preserve">External signer not found</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">432</context></context-group>
+ <note annotates="source" from="developer">&quot;External signer&quot; means using devices such as hardware wallets.</note>
+ </trans-unit>
+ <trans-unit id="_msg682">
+ <source xml:space="preserve">External signer failure</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">438</context></context-group>
+ <note annotates="source" from="developer">&quot;External signer&quot; means using devices such as hardware wallets.</note>
+ </trans-unit>
+ <trans-unit id="_msg683">
<source xml:space="preserve">Save Transaction Data</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">434</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">496</context></context-group>
</trans-unit>
- <trans-unit id="_msg667">
+ <trans-unit id="_msg684">
<source xml:space="preserve">Partially Signed Transaction (Binary)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">436</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">498</context></context-group>
<note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note>
</trans-unit>
- <trans-unit id="_msg668">
+ <trans-unit id="_msg685">
<source xml:space="preserve">PSBT saved</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">443</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
</trans-unit>
- <trans-unit id="_msg669">
+ <trans-unit id="_msg686">
+ <source xml:space="preserve">External balance:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">680</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg687">
<source xml:space="preserve">or</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">367</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">378</context></context-group>
</trans-unit>
- <trans-unit id="_msg670">
+ <trans-unit id="_msg688">
<source xml:space="preserve">You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">348</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">359</context></context-group>
</trans-unit>
- <trans-unit id="_msg671">
+ <trans-unit id="_msg689">
<source xml:space="preserve">Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
</trans-unit>
- <trans-unit id="_msg672">
+ <trans-unit id="_msg690">
<source xml:space="preserve">Please, review your transaction.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">337</context></context-group>
</trans-unit>
- <trans-unit id="_msg673">
+ <trans-unit id="_msg691">
<source xml:space="preserve">Transaction fee</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">345</context></context-group>
</trans-unit>
- <trans-unit id="_msg674">
+ <trans-unit id="_msg692">
<source xml:space="preserve">Not signalling Replace-By-Fee, BIP-125.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">350</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">361</context></context-group>
</trans-unit>
- <trans-unit id="_msg675">
+ <trans-unit id="_msg693">
<source xml:space="preserve">Total Amount</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">364</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">375</context></context-group>
</trans-unit>
- <trans-unit id="_msg676">
+ <trans-unit id="_msg694">
<source xml:space="preserve">Confirm send coins</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">389</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">400</context></context-group>
</trans-unit>
- <trans-unit id="_msg677">
+ <trans-unit id="_msg695">
<source xml:space="preserve">Confirm transaction proposal</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">389</context></context-group>
- </trans-unit>
- <trans-unit id="_msg678">
- <source xml:space="preserve">Send</source>
- <target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">400</context></context-group>
</trans-unit>
- <trans-unit id="_msg679">
+ <trans-unit id="_msg696">
<source xml:space="preserve">Watch-only balance:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">619</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">683</context></context-group>
</trans-unit>
- <trans-unit id="_msg680">
+ <trans-unit id="_msg697">
<source xml:space="preserve">The recipient address is not valid. Please recheck.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">643</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">707</context></context-group>
</trans-unit>
- <trans-unit id="_msg681">
+ <trans-unit id="_msg698">
<source xml:space="preserve">The amount to pay must be larger than 0.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">646</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">710</context></context-group>
</trans-unit>
- <trans-unit id="_msg682">
+ <trans-unit id="_msg699">
<source xml:space="preserve">The amount exceeds your balance.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">649</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">713</context></context-group>
</trans-unit>
- <trans-unit id="_msg683">
+ <trans-unit id="_msg700">
<source xml:space="preserve">The total exceeds your balance when the %1 transaction fee is included.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">652</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">716</context></context-group>
</trans-unit>
- <trans-unit id="_msg684">
+ <trans-unit id="_msg701">
<source xml:space="preserve">Duplicate address found: addresses should only be used once each.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">655</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">719</context></context-group>
</trans-unit>
- <trans-unit id="_msg685">
+ <trans-unit id="_msg702">
<source xml:space="preserve">Transaction creation failed!</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">658</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">722</context></context-group>
</trans-unit>
- <trans-unit id="_msg686">
+ <trans-unit id="_msg703">
<source xml:space="preserve">A fee higher than %1 is considered an absurdly high fee.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">662</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">726</context></context-group>
</trans-unit>
- <trans-unit id="_msg687">
+ <trans-unit id="_msg704">
<source xml:space="preserve">Payment request expired.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">665</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">729</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
- <context-group purpose="location"><context context-type="linenumber">789</context></context-group>
- <trans-unit id="_msg688[0]" approved="yes">
+ <context-group purpose="location"><context context-type="linenumber">853</context></context-group>
+ <trans-unit id="_msg705[0]" approved="yes">
<source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source>
<target xml:space="preserve">Estimated to begin confirmation within %n block.</target>
</trans-unit>
- <trans-unit id="_msg688[1]" approved="yes">
+ <trans-unit id="_msg705[1]" approved="yes">
<source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source>
<target xml:space="preserve">Estimated to begin confirmation within %n blocks.</target>
</trans-unit>
</group>
- <trans-unit id="_msg689">
+ <trans-unit id="_msg706">
<source xml:space="preserve">Warning: Invalid Bitcoin address</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">890</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">954</context></context-group>
</trans-unit>
- <trans-unit id="_msg690">
+ <trans-unit id="_msg707">
<source xml:space="preserve">Warning: Unknown change address</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">895</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">959</context></context-group>
</trans-unit>
- <trans-unit id="_msg691">
+ <trans-unit id="_msg708">
<source xml:space="preserve">Confirm custom change address</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">898</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">962</context></context-group>
</trans-unit>
- <trans-unit id="_msg692">
+ <trans-unit id="_msg709">
<source xml:space="preserve">The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">898</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">962</context></context-group>
</trans-unit>
- <trans-unit id="_msg693">
+ <trans-unit id="_msg710">
<source xml:space="preserve">(no label)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">919</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">983</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../forms/sendcoinsentry.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="SendCoinsEntry">
- <trans-unit id="_msg694" approved="yes">
+ <trans-unit id="_msg711" approved="yes">
<source xml:space="preserve">A&amp;mount:</source>
<target xml:space="preserve">A&amp;mount:</target>
<context-group purpose="location"><context context-type="linenumber">155</context></context-group>
<context-group purpose="location"><context context-type="linenumber">705</context></context-group>
<context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
</trans-unit>
- <trans-unit id="_msg695" approved="yes">
+ <trans-unit id="_msg712" approved="yes">
<source xml:space="preserve">Pay &amp;To:</source>
<target xml:space="preserve">Pay &amp;To:</target>
<context-group purpose="location"><context context-type="linenumber">39</context></context-group>
</trans-unit>
- <trans-unit id="_msg696" approved="yes">
+ <trans-unit id="_msg713" approved="yes">
<source xml:space="preserve">&amp;Label:</source>
<target xml:space="preserve">&amp;Label:</target>
<context-group purpose="location"><context context-type="linenumber">132</context></context-group>
</trans-unit>
- <trans-unit id="_msg697">
+ <trans-unit id="_msg714">
<source xml:space="preserve">Choose previously used address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">64</context></context-group>
</trans-unit>
- <trans-unit id="_msg698">
+ <trans-unit id="_msg715">
<source xml:space="preserve">The Bitcoin address to send the payment to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">57</context></context-group>
</trans-unit>
- <trans-unit id="_msg699" approved="yes">
+ <trans-unit id="_msg716" approved="yes">
<source xml:space="preserve">Alt+A</source>
<target xml:space="preserve">Alt+A</target>
<context-group purpose="location"><context context-type="linenumber">80</context></context-group>
</trans-unit>
- <trans-unit id="_msg700" approved="yes">
+ <trans-unit id="_msg717" approved="yes">
<source xml:space="preserve">Paste address from clipboard</source>
<target xml:space="preserve">Paste address from clipboard</target>
<context-group purpose="location"><context context-type="linenumber">87</context></context-group>
</trans-unit>
- <trans-unit id="_msg701" approved="yes">
+ <trans-unit id="_msg718" approved="yes">
<source xml:space="preserve">Alt+P</source>
<target xml:space="preserve">Alt+P</target>
<context-group purpose="location"><context context-type="linenumber">103</context></context-group>
</trans-unit>
- <trans-unit id="_msg702">
+ <trans-unit id="_msg719">
<source xml:space="preserve">Remove this entry</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">110</context></context-group>
<context-group purpose="location"><context context-type="linenumber">672</context></context-group>
<context-group purpose="location"><context context-type="linenumber">1205</context></context-group>
</trans-unit>
- <trans-unit id="_msg703">
+ <trans-unit id="_msg720">
<source xml:space="preserve">The amount to send in the selected unit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">170</context></context-group>
</trans-unit>
- <trans-unit id="_msg704">
+ <trans-unit id="_msg721">
<source xml:space="preserve">The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">177</context></context-group>
</trans-unit>
- <trans-unit id="_msg705">
+ <trans-unit id="_msg722">
<source xml:space="preserve">S&amp;ubtract fee from amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">180</context></context-group>
</trans-unit>
- <trans-unit id="_msg706">
+ <trans-unit id="_msg723">
<source xml:space="preserve">Use available balance</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">187</context></context-group>
</trans-unit>
- <trans-unit id="_msg707">
+ <trans-unit id="_msg724">
<source xml:space="preserve">Message:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">196</context></context-group>
</trans-unit>
- <trans-unit id="_msg708">
+ <trans-unit id="_msg725">
<source xml:space="preserve">This is an unauthenticated payment request.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">639</context></context-group>
</trans-unit>
- <trans-unit id="_msg709">
+ <trans-unit id="_msg726">
<source xml:space="preserve">This is an authenticated payment request.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">1168</context></context-group>
</trans-unit>
- <trans-unit id="_msg710">
+ <trans-unit id="_msg727">
<source xml:space="preserve">Enter a label for this address to add it to the list of used addresses</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">145</context></context-group>
<context-group purpose="location"><context context-type="linenumber">148</context></context-group>
</trans-unit>
- <trans-unit id="_msg711">
+ <trans-unit id="_msg728">
<source xml:space="preserve">A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">206</context></context-group>
</trans-unit>
- <trans-unit id="_msg712">
+ <trans-unit id="_msg729">
<source xml:space="preserve">Pay To:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">654</context></context-group>
<context-group purpose="location"><context context-type="linenumber">1183</context></context-group>
</trans-unit>
- <trans-unit id="_msg713">
+ <trans-unit id="_msg730">
<source xml:space="preserve">Memo:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">688</context></context-group>
@@ -3932,128 +4023,128 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../forms/signverifymessagedialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog">
- <trans-unit id="_msg714" approved="yes">
+ <trans-unit id="_msg731" approved="yes">
<source xml:space="preserve">Signatures - Sign / Verify a Message</source>
<target xml:space="preserve">Signatures - Sign / Verify a Message</target>
<context-group purpose="location"><context context-type="linenumber">14</context></context-group>
</trans-unit>
- <trans-unit id="_msg715" approved="yes">
+ <trans-unit id="_msg732" approved="yes">
<source xml:space="preserve">&amp;Sign Message</source>
<target xml:space="preserve">&amp;Sign Message</target>
<context-group purpose="location"><context context-type="linenumber">27</context></context-group>
</trans-unit>
- <trans-unit id="_msg716">
+ <trans-unit id="_msg733">
<source xml:space="preserve">You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">33</context></context-group>
</trans-unit>
- <trans-unit id="_msg717">
+ <trans-unit id="_msg734">
<source xml:space="preserve">The Bitcoin address to sign the message with</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">51</context></context-group>
</trans-unit>
- <trans-unit id="_msg718">
+ <trans-unit id="_msg735">
<source xml:space="preserve">Choose previously used address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">58</context></context-group>
<context-group purpose="location"><context context-type="linenumber">274</context></context-group>
</trans-unit>
- <trans-unit id="_msg719" approved="yes">
+ <trans-unit id="_msg736" approved="yes">
<source xml:space="preserve">Alt+A</source>
<target xml:space="preserve">Alt+A</target>
<context-group purpose="location"><context context-type="linenumber">68</context></context-group>
<context-group purpose="location"><context context-type="linenumber">284</context></context-group>
</trans-unit>
- <trans-unit id="_msg720" approved="yes">
+ <trans-unit id="_msg737" approved="yes">
<source xml:space="preserve">Paste address from clipboard</source>
<target xml:space="preserve">Paste address from clipboard</target>
<context-group purpose="location"><context context-type="linenumber">78</context></context-group>
</trans-unit>
- <trans-unit id="_msg721" approved="yes">
+ <trans-unit id="_msg738" approved="yes">
<source xml:space="preserve">Alt+P</source>
<target xml:space="preserve">Alt+P</target>
<context-group purpose="location"><context context-type="linenumber">88</context></context-group>
</trans-unit>
- <trans-unit id="_msg722" approved="yes">
+ <trans-unit id="_msg739" approved="yes">
<source xml:space="preserve">Enter the message you want to sign here</source>
<target xml:space="preserve">Enter the message you want to sign here</target>
<context-group purpose="location"><context context-type="linenumber">100</context></context-group>
<context-group purpose="location"><context context-type="linenumber">103</context></context-group>
</trans-unit>
- <trans-unit id="_msg723" approved="yes">
+ <trans-unit id="_msg740" approved="yes">
<source xml:space="preserve">Signature</source>
<target xml:space="preserve">Signature</target>
<context-group purpose="location"><context context-type="linenumber">110</context></context-group>
</trans-unit>
- <trans-unit id="_msg724" approved="yes">
+ <trans-unit id="_msg741" approved="yes">
<source xml:space="preserve">Copy the current signature to the system clipboard</source>
<target xml:space="preserve">Copy the current signature to the system clipboard</target>
<context-group purpose="location"><context context-type="linenumber">140</context></context-group>
</trans-unit>
- <trans-unit id="_msg725" approved="yes">
+ <trans-unit id="_msg742" approved="yes">
<source xml:space="preserve">Sign the message to prove you own this Bitcoin address</source>
<target xml:space="preserve">Sign the message to prove you own this Bitcoin address</target>
<context-group purpose="location"><context context-type="linenumber">161</context></context-group>
</trans-unit>
- <trans-unit id="_msg726" approved="yes">
+ <trans-unit id="_msg743" approved="yes">
<source xml:space="preserve">Sign &amp;Message</source>
<target xml:space="preserve">Sign &amp;Message</target>
<context-group purpose="location"><context context-type="linenumber">164</context></context-group>
</trans-unit>
- <trans-unit id="_msg727" approved="yes">
+ <trans-unit id="_msg744" approved="yes">
<source xml:space="preserve">Reset all sign message fields</source>
<target xml:space="preserve">Reset all sign message fields</target>
<context-group purpose="location"><context context-type="linenumber">178</context></context-group>
</trans-unit>
- <trans-unit id="_msg728" approved="yes">
+ <trans-unit id="_msg745" approved="yes">
<source xml:space="preserve">Clear &amp;All</source>
<target xml:space="preserve">Clear &amp;All</target>
<context-group purpose="location"><context context-type="linenumber">181</context></context-group>
<context-group purpose="location"><context context-type="linenumber">338</context></context-group>
</trans-unit>
- <trans-unit id="_msg729" approved="yes">
+ <trans-unit id="_msg746" approved="yes">
<source xml:space="preserve">&amp;Verify Message</source>
<target xml:space="preserve">&amp;Verify Message</target>
<context-group purpose="location"><context context-type="linenumber">240</context></context-group>
</trans-unit>
- <trans-unit id="_msg730">
+ <trans-unit id="_msg747">
<source xml:space="preserve">Enter the receiver&apos;s address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">246</context></context-group>
</trans-unit>
- <trans-unit id="_msg731">
+ <trans-unit id="_msg748">
<source xml:space="preserve">The Bitcoin address the message was signed with</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">267</context></context-group>
</trans-unit>
- <trans-unit id="_msg732">
+ <trans-unit id="_msg749">
<source xml:space="preserve">The signed message to verify</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">296</context></context-group>
<context-group purpose="location"><context context-type="linenumber">299</context></context-group>
</trans-unit>
- <trans-unit id="_msg733">
+ <trans-unit id="_msg750">
<source xml:space="preserve">The signature given when the message was signed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">306</context></context-group>
<context-group purpose="location"><context context-type="linenumber">309</context></context-group>
</trans-unit>
- <trans-unit id="_msg734" approved="yes">
+ <trans-unit id="_msg751" approved="yes">
<source xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</source>
<target xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</target>
<context-group purpose="location"><context context-type="linenumber">318</context></context-group>
</trans-unit>
- <trans-unit id="_msg735" approved="yes">
+ <trans-unit id="_msg752" approved="yes">
<source xml:space="preserve">Verify &amp;Message</source>
<target xml:space="preserve">Verify &amp;Message</target>
<context-group purpose="location"><context context-type="linenumber">321</context></context-group>
</trans-unit>
- <trans-unit id="_msg736" approved="yes">
+ <trans-unit id="_msg753" approved="yes">
<source xml:space="preserve">Reset all verify message fields</source>
<target xml:space="preserve">Reset all verify message fields</target>
<context-group purpose="location"><context context-type="linenumber">335</context></context-group>
</trans-unit>
- <trans-unit id="_msg737">
+ <trans-unit id="_msg754">
<source xml:space="preserve">Click &quot;Sign Message&quot; to generate signature</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">125</context></context-group>
@@ -4062,13 +4153,13 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../signverifymessagedialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog">
- <trans-unit id="_msg738">
+ <trans-unit id="_msg755">
<source xml:space="preserve">The entered address is invalid.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
<context-group purpose="location"><context context-type="linenumber">219</context></context-group>
</trans-unit>
- <trans-unit id="_msg739">
+ <trans-unit id="_msg756">
<source xml:space="preserve">Please check the address and try again.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">120</context></context-group>
@@ -4076,59 +4167,59 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<context-group purpose="location"><context context-type="linenumber">220</context></context-group>
<context-group purpose="location"><context context-type="linenumber">227</context></context-group>
</trans-unit>
- <trans-unit id="_msg740">
+ <trans-unit id="_msg757">
<source xml:space="preserve">The entered address does not refer to a key.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">127</context></context-group>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
</trans-unit>
- <trans-unit id="_msg741">
+ <trans-unit id="_msg758">
<source xml:space="preserve">Wallet unlock was cancelled.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">135</context></context-group>
</trans-unit>
- <trans-unit id="_msg742">
+ <trans-unit id="_msg759">
<source xml:space="preserve">No error</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">146</context></context-group>
</trans-unit>
- <trans-unit id="_msg743">
+ <trans-unit id="_msg760">
<source xml:space="preserve">Private key for the entered address is not available.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">149</context></context-group>
</trans-unit>
- <trans-unit id="_msg744">
+ <trans-unit id="_msg761">
<source xml:space="preserve">Message signing failed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg745">
+ <trans-unit id="_msg762">
<source xml:space="preserve">Message signed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">164</context></context-group>
</trans-unit>
- <trans-unit id="_msg746">
+ <trans-unit id="_msg763">
<source xml:space="preserve">The signature could not be decoded.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">233</context></context-group>
</trans-unit>
- <trans-unit id="_msg747">
+ <trans-unit id="_msg764">
<source xml:space="preserve">Please check the signature and try again.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">234</context></context-group>
<context-group purpose="location"><context context-type="linenumber">241</context></context-group>
</trans-unit>
- <trans-unit id="_msg748">
+ <trans-unit id="_msg765">
<source xml:space="preserve">The signature did not match the message digest.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">240</context></context-group>
</trans-unit>
- <trans-unit id="_msg749">
+ <trans-unit id="_msg766">
<source xml:space="preserve">Message verification failed.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">246</context></context-group>
</trans-unit>
- <trans-unit id="_msg750">
+ <trans-unit id="_msg767">
<source xml:space="preserve">Message verified.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">214</context></context-group>
@@ -4137,7 +4228,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../trafficgraphwidget.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="TrafficGraphWidget">
- <trans-unit id="_msg751">
+ <trans-unit id="_msg768">
<source xml:space="preserve">kB/s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">82</context></context-group>
@@ -4148,111 +4239,111 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
<group restype="x-trolltech-linguist-context" resname="TransactionDesc">
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">36</context></context-group>
- <trans-unit id="_msg752[0]" approved="yes">
+ <trans-unit id="_msg769[0]" approved="yes">
<source xml:space="preserve">Open for %n more block(s)</source>
<target xml:space="preserve">Open for %n more block</target>
</trans-unit>
- <trans-unit id="_msg752[1]" approved="yes">
+ <trans-unit id="_msg769[1]" approved="yes">
<source xml:space="preserve">Open for %n more block(s)</source>
<target xml:space="preserve">Open for %n more blocks</target>
</trans-unit>
</group>
- <trans-unit id="_msg753">
+ <trans-unit id="_msg770">
<source xml:space="preserve">Open until %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">38</context></context-group>
</trans-unit>
- <trans-unit id="_msg754">
+ <trans-unit id="_msg771">
<source xml:space="preserve">conflicted with a transaction with %1 confirmations</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">44</context></context-group>
</trans-unit>
- <trans-unit id="_msg755">
+ <trans-unit id="_msg772">
<source xml:space="preserve">0/unconfirmed, %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">47</context></context-group>
</trans-unit>
- <trans-unit id="_msg756">
+ <trans-unit id="_msg773">
<source xml:space="preserve">in memory pool</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">47</context></context-group>
</trans-unit>
- <trans-unit id="_msg757">
+ <trans-unit id="_msg774">
<source xml:space="preserve">not in memory pool</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">47</context></context-group>
</trans-unit>
- <trans-unit id="_msg758">
+ <trans-unit id="_msg775">
<source xml:space="preserve">abandoned</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">46</context></context-group>
</trans-unit>
- <trans-unit id="_msg759">
+ <trans-unit id="_msg776">
<source xml:space="preserve">%1/unconfirmed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
- <trans-unit id="_msg760">
+ <trans-unit id="_msg777">
<source xml:space="preserve">%1 confirmations</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">51</context></context-group>
</trans-unit>
- <trans-unit id="_msg761">
+ <trans-unit id="_msg778">
<source xml:space="preserve">Status</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">102</context></context-group>
</trans-unit>
- <trans-unit id="_msg762">
+ <trans-unit id="_msg779">
<source xml:space="preserve">Date</source>
<target xml:space="preserve" state="needs-review-translation">Date</target>
<context-group purpose="location"><context context-type="linenumber">105</context></context-group>
</trans-unit>
- <trans-unit id="_msg763">
+ <trans-unit id="_msg780">
<source xml:space="preserve">Source</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">112</context></context-group>
</trans-unit>
- <trans-unit id="_msg764">
+ <trans-unit id="_msg781">
<source xml:space="preserve">Generated</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">112</context></context-group>
</trans-unit>
- <trans-unit id="_msg765">
+ <trans-unit id="_msg782">
<source xml:space="preserve">From</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">117</context></context-group>
<context-group purpose="location"><context context-type="linenumber">131</context></context-group>
<context-group purpose="location"><context context-type="linenumber">203</context></context-group>
</trans-unit>
- <trans-unit id="_msg766">
+ <trans-unit id="_msg783">
<source xml:space="preserve">unknown</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">131</context></context-group>
</trans-unit>
- <trans-unit id="_msg767">
+ <trans-unit id="_msg784">
<source xml:space="preserve">To</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">132</context></context-group>
<context-group purpose="location"><context context-type="linenumber">152</context></context-group>
<context-group purpose="location"><context context-type="linenumber">222</context></context-group>
</trans-unit>
- <trans-unit id="_msg768">
+ <trans-unit id="_msg785">
<source xml:space="preserve">own address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">134</context></context-group>
</trans-unit>
- <trans-unit id="_msg769">
+ <trans-unit id="_msg786">
<source xml:space="preserve">watch-only</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">134</context></context-group>
<context-group purpose="location"><context context-type="linenumber">203</context></context-group>
</trans-unit>
- <trans-unit id="_msg770">
+ <trans-unit id="_msg787">
<source xml:space="preserve">label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">136</context></context-group>
</trans-unit>
- <trans-unit id="_msg771">
+ <trans-unit id="_msg788">
<source xml:space="preserve">Credit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">172</context></context-group>
@@ -4263,120 +4354,120 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</trans-unit>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">174</context></context-group>
- <trans-unit id="_msg772[0]" approved="yes">
+ <trans-unit id="_msg789[0]" approved="yes">
<source xml:space="preserve">matures in %n more block(s)</source>
<target xml:space="preserve">matures in %n more block</target>
</trans-unit>
- <trans-unit id="_msg772[1]" approved="yes">
+ <trans-unit id="_msg789[1]" approved="yes">
<source xml:space="preserve">matures in %n more block(s)</source>
<target xml:space="preserve">matures in %n more blocks</target>
</trans-unit>
</group>
- <trans-unit id="_msg773">
+ <trans-unit id="_msg790">
<source xml:space="preserve">not accepted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">176</context></context-group>
</trans-unit>
- <trans-unit id="_msg774">
+ <trans-unit id="_msg791">
<source xml:space="preserve">Debit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">236</context></context-group>
<context-group purpose="location"><context context-type="linenumber">262</context></context-group>
<context-group purpose="location"><context context-type="linenumber">325</context></context-group>
</trans-unit>
- <trans-unit id="_msg775">
+ <trans-unit id="_msg792">
<source xml:space="preserve">Total debit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">246</context></context-group>
</trans-unit>
- <trans-unit id="_msg776">
+ <trans-unit id="_msg793">
<source xml:space="preserve">Total credit</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">247</context></context-group>
</trans-unit>
- <trans-unit id="_msg777">
+ <trans-unit id="_msg794">
<source xml:space="preserve">Transaction fee</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">252</context></context-group>
</trans-unit>
- <trans-unit id="_msg778">
+ <trans-unit id="_msg795">
<source xml:space="preserve">Net amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">274</context></context-group>
</trans-unit>
- <trans-unit id="_msg779">
+ <trans-unit id="_msg796">
<source xml:space="preserve">Message</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">280</context></context-group>
<context-group purpose="location"><context context-type="linenumber">292</context></context-group>
</trans-unit>
- <trans-unit id="_msg780">
+ <trans-unit id="_msg797">
<source xml:space="preserve">Comment</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">282</context></context-group>
</trans-unit>
- <trans-unit id="_msg781">
+ <trans-unit id="_msg798">
<source xml:space="preserve">Transaction ID</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">284</context></context-group>
</trans-unit>
- <trans-unit id="_msg782">
+ <trans-unit id="_msg799">
<source xml:space="preserve">Transaction total size</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">285</context></context-group>
</trans-unit>
- <trans-unit id="_msg783">
+ <trans-unit id="_msg800">
<source xml:space="preserve">Transaction virtual size</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">286</context></context-group>
</trans-unit>
- <trans-unit id="_msg784">
+ <trans-unit id="_msg801">
<source xml:space="preserve">Output index</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">287</context></context-group>
</trans-unit>
- <trans-unit id="_msg785">
+ <trans-unit id="_msg802">
<source xml:space="preserve"> (Certificate was not verified)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">303</context></context-group>
</trans-unit>
- <trans-unit id="_msg786">
+ <trans-unit id="_msg803">
<source xml:space="preserve">Merchant</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">306</context></context-group>
</trans-unit>
- <trans-unit id="_msg787">
+ <trans-unit id="_msg804">
<source xml:space="preserve">Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to &quot;not accepted&quot; and it won&apos;t be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">314</context></context-group>
</trans-unit>
- <trans-unit id="_msg788">
+ <trans-unit id="_msg805">
<source xml:space="preserve">Debug information</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">322</context></context-group>
</trans-unit>
- <trans-unit id="_msg789">
+ <trans-unit id="_msg806">
<source xml:space="preserve">Transaction</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">330</context></context-group>
</trans-unit>
- <trans-unit id="_msg790">
+ <trans-unit id="_msg807">
<source xml:space="preserve">Inputs</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">333</context></context-group>
</trans-unit>
- <trans-unit id="_msg791">
+ <trans-unit id="_msg808">
<source xml:space="preserve">Amount</source>
<target xml:space="preserve" state="needs-review-translation">Amount</target>
<context-group purpose="location"><context context-type="linenumber">354</context></context-group>
</trans-unit>
- <trans-unit id="_msg792">
+ <trans-unit id="_msg809">
<source xml:space="preserve">true</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">355</context></context-group>
<context-group purpose="location"><context context-type="linenumber">356</context></context-group>
</trans-unit>
- <trans-unit id="_msg793">
+ <trans-unit id="_msg810">
<source xml:space="preserve">false</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">355</context></context-group>
@@ -4386,7 +4477,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../forms/transactiondescdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="TransactionDescDialog">
- <trans-unit id="_msg794" approved="yes">
+ <trans-unit id="_msg811" approved="yes">
<source xml:space="preserve">This pane shows a detailed description of the transaction</source>
<target xml:space="preserve">This pane shows a detailed description of the transaction</target>
<context-group purpose="location"><context context-type="linenumber">20</context></context-group>
@@ -4395,7 +4486,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../transactiondescdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="TransactionDescDialog">
- <trans-unit id="_msg795">
+ <trans-unit id="_msg812">
<source xml:space="preserve">Details for %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">18</context></context-group>
@@ -4404,138 +4495,138 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../transactiontablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="TransactionTableModel">
- <trans-unit id="_msg796">
+ <trans-unit id="_msg813">
<source xml:space="preserve">Date</source>
<target xml:space="preserve" state="needs-review-translation">Date</target>
<context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
- <trans-unit id="_msg797">
+ <trans-unit id="_msg814">
<source xml:space="preserve">Type</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
- <trans-unit id="_msg798">
+ <trans-unit id="_msg815">
<source xml:space="preserve">Label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
<group restype="x-gettext-plurals">
<context-group purpose="location"><context context-type="linenumber">320</context></context-group>
- <trans-unit id="_msg799[0]" approved="yes">
+ <trans-unit id="_msg816[0]" approved="yes">
<source xml:space="preserve">Open for %n more block(s)</source>
<target xml:space="preserve">Open for %n more block</target>
</trans-unit>
- <trans-unit id="_msg799[1]" approved="yes">
+ <trans-unit id="_msg816[1]" approved="yes">
<source xml:space="preserve">Open for %n more block(s)</source>
<target xml:space="preserve">Open for %n more blocks</target>
</trans-unit>
</group>
- <trans-unit id="_msg800">
+ <trans-unit id="_msg817">
<source xml:space="preserve">Open until %1</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">323</context></context-group>
</trans-unit>
- <trans-unit id="_msg801">
+ <trans-unit id="_msg818">
<source xml:space="preserve">Unconfirmed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">326</context></context-group>
</trans-unit>
- <trans-unit id="_msg802">
+ <trans-unit id="_msg819">
<source xml:space="preserve">Abandoned</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">329</context></context-group>
</trans-unit>
- <trans-unit id="_msg803">
+ <trans-unit id="_msg820">
<source xml:space="preserve">Confirming (%1 of %2 recommended confirmations)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">332</context></context-group>
</trans-unit>
- <trans-unit id="_msg804">
+ <trans-unit id="_msg821">
<source xml:space="preserve">Confirmed (%1 confirmations)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">335</context></context-group>
</trans-unit>
- <trans-unit id="_msg805">
+ <trans-unit id="_msg822">
<source xml:space="preserve">Conflicted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">338</context></context-group>
</trans-unit>
- <trans-unit id="_msg806">
+ <trans-unit id="_msg823">
<source xml:space="preserve">Immature (%1 confirmations, will be available after %2)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">341</context></context-group>
</trans-unit>
- <trans-unit id="_msg807">
+ <trans-unit id="_msg824">
<source xml:space="preserve">Generated but not accepted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">344</context></context-group>
</trans-unit>
- <trans-unit id="_msg808">
+ <trans-unit id="_msg825">
<source xml:space="preserve">Received with</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">383</context></context-group>
</trans-unit>
- <trans-unit id="_msg809">
+ <trans-unit id="_msg826">
<source xml:space="preserve">Received from</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">385</context></context-group>
</trans-unit>
- <trans-unit id="_msg810">
+ <trans-unit id="_msg827">
<source xml:space="preserve">Sent to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">388</context></context-group>
</trans-unit>
- <trans-unit id="_msg811">
+ <trans-unit id="_msg828">
<source xml:space="preserve">Payment to yourself</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">390</context></context-group>
</trans-unit>
- <trans-unit id="_msg812">
+ <trans-unit id="_msg829">
<source xml:space="preserve">Mined</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">392</context></context-group>
</trans-unit>
- <trans-unit id="_msg813">
+ <trans-unit id="_msg830">
<source xml:space="preserve">watch-only</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">420</context></context-group>
</trans-unit>
- <trans-unit id="_msg814">
+ <trans-unit id="_msg831">
<source xml:space="preserve">(n/a)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">436</context></context-group>
</trans-unit>
- <trans-unit id="_msg815">
+ <trans-unit id="_msg832">
<source xml:space="preserve">(no label)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">646</context></context-group>
</trans-unit>
- <trans-unit id="_msg816">
+ <trans-unit id="_msg833">
<source xml:space="preserve">Transaction status. Hover over this field to show number of confirmations.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">685</context></context-group>
</trans-unit>
- <trans-unit id="_msg817">
+ <trans-unit id="_msg834">
<source xml:space="preserve">Date and time that the transaction was received.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">687</context></context-group>
</trans-unit>
- <trans-unit id="_msg818">
+ <trans-unit id="_msg835">
<source xml:space="preserve">Type of transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">689</context></context-group>
</trans-unit>
- <trans-unit id="_msg819">
+ <trans-unit id="_msg836">
<source xml:space="preserve">Whether or not a watch-only address is involved in this transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">691</context></context-group>
</trans-unit>
- <trans-unit id="_msg820">
+ <trans-unit id="_msg837">
<source xml:space="preserve">User-defined intent/purpose of the transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">693</context></context-group>
</trans-unit>
- <trans-unit id="_msg821">
+ <trans-unit id="_msg838">
<source xml:space="preserve">Amount removed from or added to balance.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">695</context></context-group>
@@ -4544,199 +4635,199 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../transactionview.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="TransactionView">
- <trans-unit id="_msg822">
+ <trans-unit id="_msg839">
<source xml:space="preserve">All</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">70</context></context-group>
<context-group purpose="location"><context context-type="linenumber">86</context></context-group>
</trans-unit>
- <trans-unit id="_msg823">
+ <trans-unit id="_msg840">
<source xml:space="preserve">Today</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">71</context></context-group>
</trans-unit>
- <trans-unit id="_msg824">
+ <trans-unit id="_msg841">
<source xml:space="preserve">This week</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">72</context></context-group>
</trans-unit>
- <trans-unit id="_msg825">
+ <trans-unit id="_msg842">
<source xml:space="preserve">This month</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">73</context></context-group>
</trans-unit>
- <trans-unit id="_msg826">
+ <trans-unit id="_msg843">
<source xml:space="preserve">Last month</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">74</context></context-group>
</trans-unit>
- <trans-unit id="_msg827">
+ <trans-unit id="_msg844">
<source xml:space="preserve">This year</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">75</context></context-group>
</trans-unit>
- <trans-unit id="_msg828">
+ <trans-unit id="_msg845">
<source xml:space="preserve">Received with</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">87</context></context-group>
</trans-unit>
- <trans-unit id="_msg829">
+ <trans-unit id="_msg846">
<source xml:space="preserve">Sent to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">89</context></context-group>
</trans-unit>
- <trans-unit id="_msg830">
+ <trans-unit id="_msg847">
<source xml:space="preserve">To yourself</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">91</context></context-group>
</trans-unit>
- <trans-unit id="_msg831">
+ <trans-unit id="_msg848">
<source xml:space="preserve">Mined</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">92</context></context-group>
</trans-unit>
- <trans-unit id="_msg832">
+ <trans-unit id="_msg849">
<source xml:space="preserve">Other</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">93</context></context-group>
</trans-unit>
- <trans-unit id="_msg833">
+ <trans-unit id="_msg850">
<source xml:space="preserve">Enter address, transaction id, or label to search</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">98</context></context-group>
</trans-unit>
- <trans-unit id="_msg834">
+ <trans-unit id="_msg851">
<source xml:space="preserve">Min amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">102</context></context-group>
</trans-unit>
- <trans-unit id="_msg835">
- <source xml:space="preserve">Abandon transaction</source>
- <target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">177</context></context-group>
- </trans-unit>
- <trans-unit id="_msg836">
- <source xml:space="preserve">Increase transaction fee</source>
+ <trans-unit id="_msg852">
+ <source xml:space="preserve">Range…</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">76</context></context-group>
</trans-unit>
- <trans-unit id="_msg837">
- <source xml:space="preserve">Copy address</source>
+ <trans-unit id="_msg853">
+ <source xml:space="preserve">&amp;Copy address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">166</context></context-group>
</trans-unit>
- <trans-unit id="_msg838">
- <source xml:space="preserve">Copy label</source>
+ <trans-unit id="_msg854">
+ <source xml:space="preserve">Copy &amp;label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">167</context></context-group>
</trans-unit>
- <trans-unit id="_msg839">
- <source xml:space="preserve">Copy amount</source>
+ <trans-unit id="_msg855">
+ <source xml:space="preserve">Copy &amp;amount</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">168</context></context-group>
</trans-unit>
- <trans-unit id="_msg840">
- <source xml:space="preserve">Copy transaction ID</source>
+ <trans-unit id="_msg856">
+ <source xml:space="preserve">Copy transaction &amp;ID</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">169</context></context-group>
</trans-unit>
- <trans-unit id="_msg841">
- <source xml:space="preserve">Copy raw transaction</source>
+ <trans-unit id="_msg857">
+ <source xml:space="preserve">Copy &amp;raw transaction</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">170</context></context-group>
</trans-unit>
- <trans-unit id="_msg842">
- <source xml:space="preserve">Copy full transaction details</source>
+ <trans-unit id="_msg858">
+ <source xml:space="preserve">Copy full transaction &amp;details</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">171</context></context-group>
</trans-unit>
- <trans-unit id="_msg843">
- <source xml:space="preserve">Edit address label</source>
+ <trans-unit id="_msg859">
+ <source xml:space="preserve">&amp;Show transaction details</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
</trans-unit>
- <trans-unit id="_msg844">
- <source xml:space="preserve">Show transaction details</source>
+ <trans-unit id="_msg860">
+ <source xml:space="preserve">Increase transaction &amp;fee</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
</trans-unit>
- <trans-unit id="_msg845">
- <source xml:space="preserve">Range…</source>
+ <trans-unit id="_msg861">
+ <source xml:space="preserve">A&amp;bandon transaction</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">76</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">177</context></context-group>
</trans-unit>
- <trans-unit id="_msg846">
+ <trans-unit id="_msg862">
+ <source xml:space="preserve">&amp;Edit address label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg863">
<source xml:space="preserve">Export Transaction History</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">352</context></context-group>
</trans-unit>
- <trans-unit id="_msg847">
+ <trans-unit id="_msg864">
<source xml:space="preserve">Comma separated file</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">355</context></context-group>
<note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note>
</trans-unit>
- <trans-unit id="_msg848">
+ <trans-unit id="_msg865">
<source xml:space="preserve">Confirmed</source>
<target xml:space="preserve" state="needs-review-translation">Confirmed</target>
<context-group purpose="location"><context context-type="linenumber">364</context></context-group>
</trans-unit>
- <trans-unit id="_msg849">
+ <trans-unit id="_msg866">
<source xml:space="preserve">Watch-only</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">366</context></context-group>
</trans-unit>
- <trans-unit id="_msg850">
+ <trans-unit id="_msg867">
<source xml:space="preserve">Date</source>
<target xml:space="preserve" state="needs-review-translation">Date</target>
<context-group purpose="location"><context context-type="linenumber">367</context></context-group>
</trans-unit>
- <trans-unit id="_msg851">
+ <trans-unit id="_msg868">
<source xml:space="preserve">Type</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">368</context></context-group>
</trans-unit>
- <trans-unit id="_msg852">
+ <trans-unit id="_msg869">
<source xml:space="preserve">Label</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">369</context></context-group>
</trans-unit>
- <trans-unit id="_msg853">
+ <trans-unit id="_msg870">
<source xml:space="preserve">Address</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">370</context></context-group>
</trans-unit>
- <trans-unit id="_msg854">
+ <trans-unit id="_msg871">
<source xml:space="preserve">ID</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">372</context></context-group>
</trans-unit>
- <trans-unit id="_msg855">
+ <trans-unit id="_msg872">
<source xml:space="preserve">Exporting Failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">375</context></context-group>
</trans-unit>
- <trans-unit id="_msg856">
+ <trans-unit id="_msg873">
<source xml:space="preserve">There was an error trying to save the transaction history to %1.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">375</context></context-group>
</trans-unit>
- <trans-unit id="_msg857">
+ <trans-unit id="_msg874">
<source xml:space="preserve">Exporting Successful</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">379</context></context-group>
</trans-unit>
- <trans-unit id="_msg858">
+ <trans-unit id="_msg875">
<source xml:space="preserve">The transaction history was successfully saved to %1.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">379</context></context-group>
</trans-unit>
- <trans-unit id="_msg859">
+ <trans-unit id="_msg876">
<source xml:space="preserve">Range:</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">551</context></context-group>
</trans-unit>
- <trans-unit id="_msg860">
+ <trans-unit id="_msg877">
<source xml:space="preserve">to</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">559</context></context-group>
@@ -4745,178 +4836,183 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100
</body></file>
<file original="../walletframe.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="WalletFrame">
- <trans-unit id="_msg861">
+ <trans-unit id="_msg878">
<source xml:space="preserve">No wallet has been loaded.
Go to File &gt; Open Wallet to load a wallet.
- OR -</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">39</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">35</context></context-group>
</trans-unit>
- <trans-unit id="_msg862">
+ <trans-unit id="_msg879">
<source xml:space="preserve">Create a new wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">44</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">40</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../walletmodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="WalletModel">
- <trans-unit id="_msg863">
+ <trans-unit id="_msg880">
<source xml:space="preserve">Send Coins</source>
<target xml:space="preserve" state="needs-review-translation">Send Coins</target>
<context-group purpose="location"><context context-type="linenumber">218</context></context-group>
</trans-unit>
- <trans-unit id="_msg864">
+ <trans-unit id="_msg881">
<source xml:space="preserve">Fee bump error</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">497</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">549</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">562</context></context-group>
- <context-group purpose="location"><context context-type="linenumber">567</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">478</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">530</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">543</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">548</context></context-group>
</trans-unit>
- <trans-unit id="_msg865">
+ <trans-unit id="_msg882">
<source xml:space="preserve">Increasing transaction fee failed</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">497</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">478</context></context-group>
</trans-unit>
- <trans-unit id="_msg866">
+ <trans-unit id="_msg883">
<source xml:space="preserve">Do you want to increase the fee?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">486</context></context-group>
</trans-unit>
- <trans-unit id="_msg867">
+ <trans-unit id="_msg884">
<source xml:space="preserve">Do you want to draft a transaction with fee increase?</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">486</context></context-group>
</trans-unit>
- <trans-unit id="_msg868">
+ <trans-unit id="_msg885">
<source xml:space="preserve">Current fee:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">490</context></context-group>
</trans-unit>
- <trans-unit id="_msg869">
+ <trans-unit id="_msg886">
<source xml:space="preserve">Increase:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">513</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">494</context></context-group>
</trans-unit>
- <trans-unit id="_msg870">
+ <trans-unit id="_msg887">
<source xml:space="preserve">New fee:</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">517</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">498</context></context-group>
</trans-unit>
- <trans-unit id="_msg871">
+ <trans-unit id="_msg888">
<source xml:space="preserve">Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">525</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">506</context></context-group>
</trans-unit>
- <trans-unit id="_msg872">
+ <trans-unit id="_msg889">
<source xml:space="preserve">Confirm fee bump</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">528</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
</trans-unit>
- <trans-unit id="_msg873">
+ <trans-unit id="_msg890">
<source xml:space="preserve">Can&apos;t draft transaction.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">549</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">530</context></context-group>
</trans-unit>
- <trans-unit id="_msg874">
+ <trans-unit id="_msg891">
<source xml:space="preserve">PSBT copied</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">556</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">537</context></context-group>
</trans-unit>
- <trans-unit id="_msg875">
+ <trans-unit id="_msg892">
<source xml:space="preserve">Can&apos;t sign transaction.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">562</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">543</context></context-group>
</trans-unit>
- <trans-unit id="_msg876">
+ <trans-unit id="_msg893">
<source xml:space="preserve">Could not commit transaction</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">567</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">548</context></context-group>
</trans-unit>
- <trans-unit id="_msg877">
+ <trans-unit id="_msg894">
+ <source xml:space="preserve">Can&apos;t display address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">562</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg895">
<source xml:space="preserve">default wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">587</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">580</context></context-group>
</trans-unit>
</group>
</body></file>
<file original="../walletview.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="WalletView">
- <trans-unit id="_msg878">
+ <trans-unit id="_msg896">
<source xml:space="preserve">&amp;Export</source>
<target xml:space="preserve" state="needs-review-translation">&amp;Export</target>
<context-group purpose="location"><context context-type="linenumber">51</context></context-group>
</trans-unit>
- <trans-unit id="_msg879">
+ <trans-unit id="_msg897">
<source xml:space="preserve">Export the data in the current tab to a file</source>
<target xml:space="preserve" state="needs-review-translation">Export the data in the current tab to a file</target>
<context-group purpose="location"><context context-type="linenumber">52</context></context-group>
</trans-unit>
- <trans-unit id="_msg880">
+ <trans-unit id="_msg898">
<source xml:space="preserve">Error</source>
<target xml:space="preserve" state="needs-review-translation">Error</target>
<context-group purpose="location"><context context-type="linenumber">217</context></context-group>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
<context-group purpose="location"><context context-type="linenumber">236</context></context-group>
</trans-unit>
- <trans-unit id="_msg881">
+ <trans-unit id="_msg899">
<source xml:space="preserve">Unable to decode PSBT from clipboard (invalid base64)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">217</context></context-group>
</trans-unit>
- <trans-unit id="_msg882">
+ <trans-unit id="_msg900">
<source xml:space="preserve">Load Transaction Data</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">222</context></context-group>
</trans-unit>
- <trans-unit id="_msg883">
+ <trans-unit id="_msg901">
<source xml:space="preserve">Partially Signed Transaction (*.psbt)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">223</context></context-group>
</trans-unit>
- <trans-unit id="_msg884">
+ <trans-unit id="_msg902">
<source xml:space="preserve">PSBT file must be smaller than 100 MiB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
</trans-unit>
- <trans-unit id="_msg885">
+ <trans-unit id="_msg903">
<source xml:space="preserve">Unable to decode PSBT</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">236</context></context-group>
</trans-unit>
- <trans-unit id="_msg886">
+ <trans-unit id="_msg904">
<source xml:space="preserve">Backup Wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">275</context></context-group>
</trans-unit>
- <trans-unit id="_msg887">
+ <trans-unit id="_msg905">
<source xml:space="preserve">Wallet Data</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">277</context></context-group>
<note annotates="source" from="developer">Name of the wallet data file format.</note>
</trans-unit>
- <trans-unit id="_msg888">
+ <trans-unit id="_msg906">
<source xml:space="preserve">Backup Failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">283</context></context-group>
</trans-unit>
- <trans-unit id="_msg889">
+ <trans-unit id="_msg907">
<source xml:space="preserve">There was an error trying to save the wallet data to %1.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">283</context></context-group>
</trans-unit>
- <trans-unit id="_msg890">
+ <trans-unit id="_msg908">
<source xml:space="preserve">Backup Successful</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">287</context></context-group>
</trans-unit>
- <trans-unit id="_msg891">
+ <trans-unit id="_msg909">
<source xml:space="preserve">The wallet data was successfully saved to %1.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">287</context></context-group>
</trans-unit>
- <trans-unit id="_msg892">
+ <trans-unit id="_msg910">
<source xml:space="preserve">Cancel</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">331</context></context-group>
@@ -4925,766 +5021,776 @@ Go to File &gt; Open Wallet to load a wallet.
</body></file>
<file original="../bitcoinstrings.cpp" datatype="cpp" source-language="en" target-language="en"><body>
<group restype="x-trolltech-linguist-context" resname="bitcoin-core">
- <trans-unit id="_msg893">
+ <trans-unit id="_msg911">
<source xml:space="preserve">The %s developers</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">12</context></context-group>
</trans-unit>
- <trans-unit id="_msg894">
+ <trans-unit id="_msg912">
<source xml:space="preserve">%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">13</context></context-group>
</trans-unit>
- <trans-unit id="_msg895">
+ <trans-unit id="_msg913">
<source xml:space="preserve">-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">16</context></context-group>
</trans-unit>
- <trans-unit id="_msg896">
+ <trans-unit id="_msg914">
<source xml:space="preserve">Cannot downgrade wallet from version %i to version %i. Wallet version unchanged.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">19</context></context-group>
</trans-unit>
- <trans-unit id="_msg897">
+ <trans-unit id="_msg915">
<source xml:space="preserve">Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">22</context></context-group>
</trans-unit>
- <trans-unit id="_msg898">
+ <trans-unit id="_msg916">
<source xml:space="preserve">Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">24</context></context-group>
</trans-unit>
- <trans-unit id="_msg899">
+ <trans-unit id="_msg917">
<source xml:space="preserve">Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">27</context></context-group>
</trans-unit>
- <trans-unit id="_msg900">
+ <trans-unit id="_msg918">
<source xml:space="preserve">Distributed under the MIT software license, see the accompanying file %s or %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">31</context></context-group>
</trans-unit>
- <trans-unit id="_msg901">
+ <trans-unit id="_msg919">
<source xml:space="preserve">Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">34</context></context-group>
</trans-unit>
- <trans-unit id="_msg902">
+ <trans-unit id="_msg920">
<source xml:space="preserve">Error: Dumpfile format record is incorrect. Got &quot;%s&quot;, expected &quot;format&quot;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">37</context></context-group>
</trans-unit>
- <trans-unit id="_msg903">
+ <trans-unit id="_msg921">
<source xml:space="preserve">Error: Dumpfile identifier record is incorrect. Got &quot;%s&quot;, expected &quot;%s&quot;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">39</context></context-group>
</trans-unit>
- <trans-unit id="_msg904">
+ <trans-unit id="_msg922">
<source xml:space="preserve">Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">41</context></context-group>
</trans-unit>
- <trans-unit id="_msg905">
- <source xml:space="preserve">Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <trans-unit id="_msg923">
+ <source xml:space="preserve">Error: Legacy wallets only support the &quot;legacy&quot;, &quot;p2sh-segwit&quot;, and &quot;bech32&quot; address types</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">44</context></context-group>
</trans-unit>
- <trans-unit id="_msg906">
+ <trans-unit id="_msg924">
+ <source xml:space="preserve">Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg925">
<source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">46</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
- <trans-unit id="_msg907">
+ <trans-unit id="_msg926">
<source xml:space="preserve">File %s already exists. If you are sure this is what you want, move it out of the way first.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">52</context></context-group>
</trans-unit>
- <trans-unit id="_msg908">
+ <trans-unit id="_msg927">
<source xml:space="preserve">Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">52</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">55</context></context-group>
</trans-unit>
- <trans-unit id="_msg909">
+ <trans-unit id="_msg928">
<source xml:space="preserve">More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">55</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
</trans-unit>
- <trans-unit id="_msg910">
+ <trans-unit id="_msg929">
<source xml:space="preserve">No dump file provided. To use createfromdump, -dumpfile=&lt;filename&gt; must be provided.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">61</context></context-group>
</trans-unit>
- <trans-unit id="_msg911">
+ <trans-unit id="_msg930">
<source xml:space="preserve">No dump file provided. To use dump, -dumpfile=&lt;filename&gt; must be provided.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">61</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">64</context></context-group>
</trans-unit>
- <trans-unit id="_msg912">
+ <trans-unit id="_msg931">
<source xml:space="preserve">No wallet file format provided. To use createfromdump, -format=&lt;format&gt; must be provided.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">63</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
</trans-unit>
- <trans-unit id="_msg913">
+ <trans-unit id="_msg932">
<source xml:space="preserve">Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
</trans-unit>
- <trans-unit id="_msg914">
+ <trans-unit id="_msg933">
<source xml:space="preserve">Please contribute if you find %s useful. Visit %s for further information about the software.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">72</context></context-group>
</trans-unit>
- <trans-unit id="_msg915">
+ <trans-unit id="_msg934">
<source xml:space="preserve">Prune configured below the minimum of %d MiB. Please use a higher number.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">72</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">75</context></context-group>
</trans-unit>
- <trans-unit id="_msg916">
+ <trans-unit id="_msg935">
<source xml:space="preserve">Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">74</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">77</context></context-group>
</trans-unit>
- <trans-unit id="_msg917">
+ <trans-unit id="_msg936">
<source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">77</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
</trans-unit>
- <trans-unit id="_msg918">
+ <trans-unit id="_msg937">
<source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer&apos;s date and time being set incorrectly. Only rebuild the block database if you are sure that your computer&apos;s date and time are correct</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">83</context></context-group>
</trans-unit>
- <trans-unit id="_msg919">
+ <trans-unit id="_msg938">
<source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
</trans-unit>
- <trans-unit id="_msg920">
+ <trans-unit id="_msg939">
<source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
- <trans-unit id="_msg921">
+ <trans-unit id="_msg940">
<source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">91</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">94</context></context-group>
</trans-unit>
- <trans-unit id="_msg922">
+ <trans-unit id="_msg941">
<source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">94</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
</trans-unit>
- <trans-unit id="_msg923">
+ <trans-unit id="_msg942">
<source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">100</context></context-group>
</trans-unit>
- <trans-unit id="_msg924">
+ <trans-unit id="_msg943">
<source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">100</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
</trans-unit>
- <trans-unit id="_msg925">
+ <trans-unit id="_msg944">
<source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
<target xml:space="preserve"></target>
- <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
- </trans-unit>
- <trans-unit id="_msg926">
- <source xml:space="preserve">Transaction needs a change address, but we can&apos;t generate it. Please call keypoolrefill first.</source>
- <target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">105</context></context-group>
</trans-unit>
- <trans-unit id="_msg927">
+ <trans-unit id="_msg945">
<source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">108</context></context-group>
</trans-unit>
- <trans-unit id="_msg928">
+ <trans-unit id="_msg946">
<source xml:space="preserve">Unknown wallet file format &quot;%s&quot; provided. Please provide one of &quot;bdb&quot; or &quot;sqlite&quot;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
- <trans-unit id="_msg929">
+ <trans-unit id="_msg947">
<source xml:space="preserve">Warning: Dumpfile wallet format &quot;%s&quot; does not match command line specified format &quot;%s&quot;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">114</context></context-group>
</trans-unit>
- <trans-unit id="_msg930">
+ <trans-unit id="_msg948">
<source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">117</context></context-group>
</trans-unit>
- <trans-unit id="_msg931">
+ <trans-unit id="_msg949">
<source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">119</context></context-group>
</trans-unit>
- <trans-unit id="_msg932">
+ <trans-unit id="_msg950">
<source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">122</context></context-group>
</trans-unit>
- <trans-unit id="_msg933">
+ <trans-unit id="_msg951">
<source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">125</context></context-group>
</trans-unit>
- <trans-unit id="_msg934">
+ <trans-unit id="_msg952">
<source xml:space="preserve">%s is set very high!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">128</context></context-group>
</trans-unit>
- <trans-unit id="_msg935">
+ <trans-unit id="_msg953">
<source xml:space="preserve">-maxmempool must be at least %d MB</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">129</context></context-group>
</trans-unit>
- <trans-unit id="_msg936">
+ <trans-unit id="_msg954">
<source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">130</context></context-group>
</trans-unit>
- <trans-unit id="_msg937">
+ <trans-unit id="_msg955">
<source xml:space="preserve">Cannot resolve -%s address: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">131</context></context-group>
</trans-unit>
- <trans-unit id="_msg938">
+ <trans-unit id="_msg956">
<source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">132</context></context-group>
</trans-unit>
- <trans-unit id="_msg939">
+ <trans-unit id="_msg957">
<source xml:space="preserve">Cannot write to data directory &apos;%s&apos;; check permissions.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">133</context></context-group>
</trans-unit>
- <trans-unit id="_msg940">
+ <trans-unit id="_msg958">
<source xml:space="preserve">Change index out of range</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">134</context></context-group>
</trans-unit>
- <trans-unit id="_msg941">
+ <trans-unit id="_msg959">
<source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">135</context></context-group>
</trans-unit>
- <trans-unit id="_msg942">
+ <trans-unit id="_msg960">
<source xml:space="preserve">Copyright (C) %i-%i</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">136</context></context-group>
</trans-unit>
- <trans-unit id="_msg943">
+ <trans-unit id="_msg961">
<source xml:space="preserve">Corrupted block database detected</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">137</context></context-group>
</trans-unit>
- <trans-unit id="_msg944">
+ <trans-unit id="_msg962">
<source xml:space="preserve">Could not find asmap file %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg945">
+ <trans-unit id="_msg963">
<source xml:space="preserve">Could not parse asmap file %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">139</context></context-group>
</trans-unit>
- <trans-unit id="_msg946">
+ <trans-unit id="_msg964">
<source xml:space="preserve">Disk space is too low!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">140</context></context-group>
</trans-unit>
- <trans-unit id="_msg947">
+ <trans-unit id="_msg965">
<source xml:space="preserve">Do you want to rebuild the block database now?</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">141</context></context-group>
</trans-unit>
- <trans-unit id="_msg948">
+ <trans-unit id="_msg966">
<source xml:space="preserve">Done loading</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">142</context></context-group>
</trans-unit>
- <trans-unit id="_msg949">
+ <trans-unit id="_msg967">
<source xml:space="preserve">Dump file %s does not exist.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">143</context></context-group>
</trans-unit>
- <trans-unit id="_msg950">
+ <trans-unit id="_msg968">
<source xml:space="preserve">Error creating %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">144</context></context-group>
</trans-unit>
- <trans-unit id="_msg951">
+ <trans-unit id="_msg969">
<source xml:space="preserve">Error initializing block database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">145</context></context-group>
</trans-unit>
- <trans-unit id="_msg952">
+ <trans-unit id="_msg970">
<source xml:space="preserve">Error initializing wallet database environment %s!</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">146</context></context-group>
</trans-unit>
- <trans-unit id="_msg953">
+ <trans-unit id="_msg971">
<source xml:space="preserve">Error loading %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">147</context></context-group>
</trans-unit>
- <trans-unit id="_msg954">
+ <trans-unit id="_msg972">
<source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">148</context></context-group>
</trans-unit>
- <trans-unit id="_msg955">
+ <trans-unit id="_msg973">
<source xml:space="preserve">Error loading %s: Wallet corrupted</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">149</context></context-group>
</trans-unit>
- <trans-unit id="_msg956">
+ <trans-unit id="_msg974">
<source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">150</context></context-group>
</trans-unit>
- <trans-unit id="_msg957">
+ <trans-unit id="_msg975">
<source xml:space="preserve">Error loading block database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">151</context></context-group>
</trans-unit>
- <trans-unit id="_msg958">
+ <trans-unit id="_msg976">
<source xml:space="preserve">Error opening block database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg959">
+ <trans-unit id="_msg977">
<source xml:space="preserve">Error reading from database, shutting down.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">153</context></context-group>
</trans-unit>
- <trans-unit id="_msg960">
+ <trans-unit id="_msg978">
<source xml:space="preserve">Error reading next record from wallet database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">154</context></context-group>
</trans-unit>
- <trans-unit id="_msg961">
+ <trans-unit id="_msg979">
<source xml:space="preserve">Error upgrading chainstate database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">155</context></context-group>
</trans-unit>
- <trans-unit id="_msg962">
+ <trans-unit id="_msg980">
<source xml:space="preserve">Error: Couldn&apos;t create cursor into database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">156</context></context-group>
</trans-unit>
- <trans-unit id="_msg963">
+ <trans-unit id="_msg981">
<source xml:space="preserve">Error: Disk space is low for %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">157</context></context-group>
</trans-unit>
- <trans-unit id="_msg964">
+ <trans-unit id="_msg982">
<source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">158</context></context-group>
</trans-unit>
- <trans-unit id="_msg965">
+ <trans-unit id="_msg983">
<source xml:space="preserve">Error: Got key that was not hex: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">159</context></context-group>
</trans-unit>
- <trans-unit id="_msg966">
+ <trans-unit id="_msg984">
<source xml:space="preserve">Error: Got value that was not hex: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">160</context></context-group>
</trans-unit>
- <trans-unit id="_msg967">
+ <trans-unit id="_msg985">
<source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">161</context></context-group>
</trans-unit>
- <trans-unit id="_msg968">
+ <trans-unit id="_msg986">
<source xml:space="preserve">Error: Missing checksum</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">162</context></context-group>
</trans-unit>
- <trans-unit id="_msg969">
- <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source>
+ <trans-unit id="_msg987">
+ <source xml:space="preserve">Error: No %s addresses available.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">163</context></context-group>
</trans-unit>
- <trans-unit id="_msg970">
- <source xml:space="preserve">Error: Unable to write record to new wallet</source>
+ <trans-unit id="_msg988">
+ <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">164</context></context-group>
</trans-unit>
- <trans-unit id="_msg971">
- <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source>
+ <trans-unit id="_msg989">
+ <source xml:space="preserve">Error: Unable to write record to new wallet</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">165</context></context-group>
</trans-unit>
- <trans-unit id="_msg972">
- <source xml:space="preserve">Failed to rescan the wallet during initialization</source>
+ <trans-unit id="_msg990">
+ <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">166</context></context-group>
</trans-unit>
- <trans-unit id="_msg973">
- <source xml:space="preserve">Failed to verify database</source>
+ <trans-unit id="_msg991">
+ <source xml:space="preserve">Failed to rescan the wallet during initialization</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">167</context></context-group>
</trans-unit>
- <trans-unit id="_msg974">
- <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
+ <trans-unit id="_msg992">
+ <source xml:space="preserve">Failed to verify database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">168</context></context-group>
</trans-unit>
- <trans-unit id="_msg975">
- <source xml:space="preserve">Ignoring duplicate -wallet %s.</source>
+ <trans-unit id="_msg993">
+ <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">169</context></context-group>
</trans-unit>
- <trans-unit id="_msg976">
- <source xml:space="preserve">Importing…</source>
+ <trans-unit id="_msg994">
+ <source xml:space="preserve">Ignoring duplicate -wallet %s.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">170</context></context-group>
</trans-unit>
- <trans-unit id="_msg977">
- <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <trans-unit id="_msg995">
+ <source xml:space="preserve">Importing…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">171</context></context-group>
</trans-unit>
- <trans-unit id="_msg978">
- <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source>
+ <trans-unit id="_msg996">
+ <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">172</context></context-group>
</trans-unit>
- <trans-unit id="_msg979">
- <source xml:space="preserve">Insufficient funds</source>
+ <trans-unit id="_msg997">
+ <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">173</context></context-group>
</trans-unit>
- <trans-unit id="_msg980">
- <source xml:space="preserve">Invalid -i2psam address or hostname: &apos;%s&apos;</source>
+ <trans-unit id="_msg998">
+ <source xml:space="preserve">Insufficient funds</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">174</context></context-group>
</trans-unit>
- <trans-unit id="_msg981">
- <source xml:space="preserve">Invalid -onion address or hostname: &apos;%s&apos;</source>
+ <trans-unit id="_msg999">
+ <source xml:space="preserve">Invalid -i2psam address or hostname: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">175</context></context-group>
</trans-unit>
- <trans-unit id="_msg982">
- <source xml:space="preserve">Invalid -proxy address or hostname: &apos;%s&apos;</source>
+ <trans-unit id="_msg1000">
+ <source xml:space="preserve">Invalid -onion address or hostname: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">176</context></context-group>
</trans-unit>
- <trans-unit id="_msg983">
- <source xml:space="preserve">Invalid P2P permission: &apos;%s&apos;</source>
+ <trans-unit id="_msg1001">
+ <source xml:space="preserve">Invalid -proxy address or hostname: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">177</context></context-group>
</trans-unit>
- <trans-unit id="_msg984">
- <source xml:space="preserve">Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <trans-unit id="_msg1002">
+ <source xml:space="preserve">Invalid P2P permission: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">178</context></context-group>
</trans-unit>
- <trans-unit id="_msg985">
- <source xml:space="preserve">Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <trans-unit id="_msg1003">
+ <source xml:space="preserve">Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">179</context></context-group>
</trans-unit>
- <trans-unit id="_msg986">
- <source xml:space="preserve">Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <trans-unit id="_msg1004">
+ <source xml:space="preserve">Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">180</context></context-group>
</trans-unit>
- <trans-unit id="_msg987">
- <source xml:space="preserve">Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <trans-unit id="_msg1005">
+ <source xml:space="preserve">Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">181</context></context-group>
</trans-unit>
- <trans-unit id="_msg988">
- <source xml:space="preserve">Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
+ <trans-unit id="_msg1006">
+ <source xml:space="preserve">Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">182</context></context-group>
</trans-unit>
- <trans-unit id="_msg989">
- <source xml:space="preserve">Loading P2P addresses…</source>
+ <trans-unit id="_msg1007">
+ <source xml:space="preserve">Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">183</context></context-group>
</trans-unit>
- <trans-unit id="_msg990">
- <source xml:space="preserve">Loading banlist…</source>
+ <trans-unit id="_msg1008">
+ <source xml:space="preserve">Loading P2P addresses…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">184</context></context-group>
</trans-unit>
- <trans-unit id="_msg991">
- <source xml:space="preserve">Loading block index…</source>
+ <trans-unit id="_msg1009">
+ <source xml:space="preserve">Loading banlist…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">185</context></context-group>
</trans-unit>
- <trans-unit id="_msg992">
- <source xml:space="preserve">Loading wallet…</source>
+ <trans-unit id="_msg1010">
+ <source xml:space="preserve">Loading block index…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">186</context></context-group>
</trans-unit>
- <trans-unit id="_msg993">
- <source xml:space="preserve">Need to specify a port with -whitebind: &apos;%s&apos;</source>
+ <trans-unit id="_msg1011">
+ <source xml:space="preserve">Loading wallet…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">187</context></context-group>
</trans-unit>
- <trans-unit id="_msg994">
- <source xml:space="preserve">No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
+ <trans-unit id="_msg1012">
+ <source xml:space="preserve">Need to specify a port with -whitebind: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">188</context></context-group>
</trans-unit>
- <trans-unit id="_msg995">
- <source xml:space="preserve">Not enough file descriptors available.</source>
+ <trans-unit id="_msg1013">
+ <source xml:space="preserve">No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">189</context></context-group>
</trans-unit>
- <trans-unit id="_msg996">
- <source xml:space="preserve">Prune cannot be configured with a negative value.</source>
+ <trans-unit id="_msg1014">
+ <source xml:space="preserve">Not enough file descriptors available.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">190</context></context-group>
</trans-unit>
- <trans-unit id="_msg997">
- <source xml:space="preserve">Prune mode is incompatible with -coinstatsindex.</source>
+ <trans-unit id="_msg1015">
+ <source xml:space="preserve">Prune cannot be configured with a negative value.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">191</context></context-group>
</trans-unit>
- <trans-unit id="_msg998">
- <source xml:space="preserve">Prune mode is incompatible with -txindex.</source>
+ <trans-unit id="_msg1016">
+ <source xml:space="preserve">Prune mode is incompatible with -coinstatsindex.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">192</context></context-group>
</trans-unit>
- <trans-unit id="_msg999">
- <source xml:space="preserve">Pruning blockstore…</source>
+ <trans-unit id="_msg1017">
+ <source xml:space="preserve">Prune mode is incompatible with -txindex.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">193</context></context-group>
</trans-unit>
- <trans-unit id="_msg1000">
- <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <trans-unit id="_msg1018">
+ <source xml:space="preserve">Pruning blockstore…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">194</context></context-group>
</trans-unit>
- <trans-unit id="_msg1001">
- <source xml:space="preserve">Replaying blocks…</source>
+ <trans-unit id="_msg1019">
+ <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">195</context></context-group>
</trans-unit>
- <trans-unit id="_msg1002">
- <source xml:space="preserve">Rescanning…</source>
+ <trans-unit id="_msg1020">
+ <source xml:space="preserve">Replaying blocks…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">196</context></context-group>
</trans-unit>
- <trans-unit id="_msg1003">
- <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source>
+ <trans-unit id="_msg1021">
+ <source xml:space="preserve">Rescanning…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">197</context></context-group>
</trans-unit>
- <trans-unit id="_msg1004">
- <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
+ <trans-unit id="_msg1022">
+ <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">198</context></context-group>
</trans-unit>
- <trans-unit id="_msg1005">
- <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source>
+ <trans-unit id="_msg1023">
+ <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">199</context></context-group>
</trans-unit>
- <trans-unit id="_msg1006">
- <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
+ <trans-unit id="_msg1024">
+ <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">200</context></context-group>
</trans-unit>
- <trans-unit id="_msg1007">
- <source xml:space="preserve">Section [%s] is not recognized.</source>
+ <trans-unit id="_msg1025">
+ <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">201</context></context-group>
</trans-unit>
- <trans-unit id="_msg1008">
- <source xml:space="preserve">Signing transaction failed</source>
+ <trans-unit id="_msg1026">
+ <source xml:space="preserve">Section [%s] is not recognized.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">202</context></context-group>
</trans-unit>
- <trans-unit id="_msg1009">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; does not exist</source>
+ <trans-unit id="_msg1027">
+ <source xml:space="preserve">Signing transaction failed</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">203</context></context-group>
</trans-unit>
- <trans-unit id="_msg1010">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is a relative path</source>
+ <trans-unit id="_msg1028">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; does not exist</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">204</context></context-group>
</trans-unit>
- <trans-unit id="_msg1011">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is not a directory</source>
+ <trans-unit id="_msg1029">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is a relative path</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">205</context></context-group>
</trans-unit>
- <trans-unit id="_msg1012">
- <source xml:space="preserve">Specified blocks directory &quot;%s&quot; does not exist.</source>
+ <trans-unit id="_msg1030">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is not a directory</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">206</context></context-group>
</trans-unit>
- <trans-unit id="_msg1013">
- <source xml:space="preserve">Starting network threads…</source>
+ <trans-unit id="_msg1031">
+ <source xml:space="preserve">Specified blocks directory &quot;%s&quot; does not exist.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">207</context></context-group>
</trans-unit>
- <trans-unit id="_msg1014">
- <source xml:space="preserve">The source code is available from %s.</source>
+ <trans-unit id="_msg1032">
+ <source xml:space="preserve">Starting network threads…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">208</context></context-group>
</trans-unit>
- <trans-unit id="_msg1015">
- <source xml:space="preserve">The specified config file %s does not exist</source>
+ <trans-unit id="_msg1033">
+ <source xml:space="preserve">The source code is available from %s.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">209</context></context-group>
</trans-unit>
- <trans-unit id="_msg1016">
- <source xml:space="preserve">The transaction amount is too small to pay the fee</source>
+ <trans-unit id="_msg1034">
+ <source xml:space="preserve">The specified config file %s does not exist</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">210</context></context-group>
</trans-unit>
- <trans-unit id="_msg1017">
- <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source>
+ <trans-unit id="_msg1035">
+ <source xml:space="preserve">The transaction amount is too small to pay the fee</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">211</context></context-group>
</trans-unit>
- <trans-unit id="_msg1018">
- <source xml:space="preserve">This is experimental software.</source>
+ <trans-unit id="_msg1036">
+ <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">212</context></context-group>
</trans-unit>
- <trans-unit id="_msg1019">
- <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source>
+ <trans-unit id="_msg1037">
+ <source xml:space="preserve">This is experimental software.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">213</context></context-group>
</trans-unit>
- <trans-unit id="_msg1020">
- <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source>
+ <trans-unit id="_msg1038">
+ <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">214</context></context-group>
</trans-unit>
- <trans-unit id="_msg1021">
- <source xml:space="preserve">Transaction amount too small</source>
+ <trans-unit id="_msg1039">
+ <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg1022">
- <source xml:space="preserve">Transaction amounts must not be negative</source>
+ <trans-unit id="_msg1040">
+ <source xml:space="preserve">Transaction amount too small</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">216</context></context-group>
</trans-unit>
- <trans-unit id="_msg1023">
- <source xml:space="preserve">Transaction has too long of a mempool chain</source>
+ <trans-unit id="_msg1041">
+ <source xml:space="preserve">Transaction amounts must not be negative</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">217</context></context-group>
</trans-unit>
- <trans-unit id="_msg1024">
- <source xml:space="preserve">Transaction must have at least one recipient</source>
+ <trans-unit id="_msg1042">
+ <source xml:space="preserve">Transaction has too long of a mempool chain</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">218</context></context-group>
</trans-unit>
- <trans-unit id="_msg1025">
- <source xml:space="preserve">Transaction too large</source>
+ <trans-unit id="_msg1043">
+ <source xml:space="preserve">Transaction must have at least one recipient</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">219</context></context-group>
</trans-unit>
- <trans-unit id="_msg1026">
- <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source>
+ <trans-unit id="_msg1044">
+ <source xml:space="preserve">Transaction needs a change address, but we can&apos;t generate it. %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">220</context></context-group>
</trans-unit>
- <trans-unit id="_msg1027">
- <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source>
+ <trans-unit id="_msg1045">
+ <source xml:space="preserve">Transaction too large</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">221</context></context-group>
</trans-unit>
- <trans-unit id="_msg1028">
- <source xml:space="preserve">Unable to create the PID file &apos;%s&apos;: %s</source>
+ <trans-unit id="_msg1046">
+ <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">222</context></context-group>
</trans-unit>
- <trans-unit id="_msg1029">
- <source xml:space="preserve">Unable to generate initial keys</source>
+ <trans-unit id="_msg1047">
+ <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">223</context></context-group>
</trans-unit>
- <trans-unit id="_msg1030">
- <source xml:space="preserve">Unable to generate keys</source>
+ <trans-unit id="_msg1048">
+ <source xml:space="preserve">Unable to create the PID file &apos;%s&apos;: %s</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">224</context></context-group>
</trans-unit>
- <trans-unit id="_msg1031">
- <source xml:space="preserve">Unable to open %s for writing</source>
+ <trans-unit id="_msg1049">
+ <source xml:space="preserve">Unable to generate initial keys</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">225</context></context-group>
</trans-unit>
- <trans-unit id="_msg1032">
- <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source>
+ <trans-unit id="_msg1050">
+ <source xml:space="preserve">Unable to generate keys</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
</trans-unit>
- <trans-unit id="_msg1033">
- <source xml:space="preserve">Unknown -blockfilterindex value %s.</source>
+ <trans-unit id="_msg1051">
+ <source xml:space="preserve">Unable to open %s for writing</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">227</context></context-group>
</trans-unit>
- <trans-unit id="_msg1034">
- <source xml:space="preserve">Unknown address type &apos;%s&apos;</source>
+ <trans-unit id="_msg1052">
+ <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">228</context></context-group>
</trans-unit>
- <trans-unit id="_msg1035">
- <source xml:space="preserve">Unknown change type &apos;%s&apos;</source>
+ <trans-unit id="_msg1053">
+ <source xml:space="preserve">Unknown -blockfilterindex value %s.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">229</context></context-group>
</trans-unit>
- <trans-unit id="_msg1036">
- <source xml:space="preserve">Unknown network specified in -onlynet: &apos;%s&apos;</source>
+ <trans-unit id="_msg1054">
+ <source xml:space="preserve">Unknown address type &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">230</context></context-group>
</trans-unit>
- <trans-unit id="_msg1037">
- <source xml:space="preserve">Unsupported logging category %s=%s.</source>
+ <trans-unit id="_msg1055">
+ <source xml:space="preserve">Unknown change type &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">231</context></context-group>
</trans-unit>
- <trans-unit id="_msg1038">
- <source xml:space="preserve">Upgrading UTXO database</source>
+ <trans-unit id="_msg1056">
+ <source xml:space="preserve">Unknown network specified in -onlynet: &apos;%s&apos;</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">232</context></context-group>
</trans-unit>
- <trans-unit id="_msg1039">
- <source xml:space="preserve">Upgrading txindex database</source>
+ <trans-unit id="_msg1057">
+ <source xml:space="preserve">Unknown new rules activated (versionbit %i)</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">233</context></context-group>
</trans-unit>
- <trans-unit id="_msg1040">
- <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source>
+ <trans-unit id="_msg1058">
+ <source xml:space="preserve">Unsupported logging category %s=%s.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">234</context></context-group>
</trans-unit>
- <trans-unit id="_msg1041">
- <source xml:space="preserve">Verifying blocks…</source>
+ <trans-unit id="_msg1059">
+ <source xml:space="preserve">Upgrading UTXO database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">235</context></context-group>
</trans-unit>
- <trans-unit id="_msg1042">
- <source xml:space="preserve">Verifying wallet(s)…</source>
+ <trans-unit id="_msg1060">
+ <source xml:space="preserve">Upgrading txindex database</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">236</context></context-group>
</trans-unit>
- <trans-unit id="_msg1043">
- <source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source>
+ <trans-unit id="_msg1061">
+ <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">237</context></context-group>
</trans-unit>
- <trans-unit id="_msg1044">
- <source xml:space="preserve">Warning: unknown new rules activated (versionbit %i)</source>
+ <trans-unit id="_msg1062">
+ <source xml:space="preserve">Verifying blocks…</source>
<target xml:space="preserve"></target>
<context-group purpose="location"><context context-type="linenumber">238</context></context-group>
</trans-unit>
+ <trans-unit id="_msg1063">
+ <source xml:space="preserve">Verifying wallet(s)…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">239</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1064">
+ <source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ </trans-unit>
</group>
</body></file>
</xliff>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 6ad8db4348..b12fe96567 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -92,6 +92,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->thirdPartyTxUrls->setVisible(false);
}
+#ifndef ENABLE_EXTERNAL_SIGNER
+ //: "External signing" means using devices such as hardware wallets.
+ ui->externalSignerPath->setToolTip(tr("Compiled without external signing support (required for external signing)"));
+ ui->externalSignerPath->setEnabled(false);
+#endif
/* Display elements init */
QDir translations(":translations");
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 27783bdf87..864a62edc8 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -69,20 +69,18 @@ public:
foreground = brush.color();
}
- painter->setPen(foreground);
- QRect boundingRect;
- painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect);
- int address_rect_min_width = boundingRect.width();
-
- if (index.data(TransactionTableModel::WatchonlyRole).toBool())
- {
+ if (index.data(TransactionTableModel::WatchonlyRole).toBool()) {
QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole));
- QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight);
+ QRect watchonlyRect(addressRect.left(), addressRect.top(), 16, addressRect.height());
iconWatchonly = platformStyle->TextColorIcon(iconWatchonly);
iconWatchonly.paint(painter, watchonlyRect);
- address_rect_min_width += 5 + watchonlyRect.width();
+ addressRect.setLeft(addressRect.left() + watchonlyRect.width() + 5);
}
+ painter->setPen(foreground);
+ QRect boundingRect;
+ painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect);
+
if(amount < 0)
{
foreground = COLOR_NEGATIVE;
@@ -109,7 +107,8 @@ public:
QRect date_bounding_rect;
painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date), &date_bounding_rect);
- const int minimum_width = std::max(address_rect_min_width, amount_bounding_rect.width() + date_bounding_rect.width());
+ // 0.4*date_bounding_rect.width() is used to visually distinguish a date from an amount.
+ const int minimum_width = 1.4 * date_bounding_rect.width() + amount_bounding_rect.width();
const auto search = m_minimum_width.find(index.row());
if (search == m_minimum_width.end() || search->second != minimum_width) {
m_minimum_width[index.row()] = minimum_width;
@@ -166,8 +165,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
- connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
- connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
+ connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
+ connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
@@ -176,11 +175,6 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
-void OverviewPage::handleOutOfSyncWarningClicks()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
-
void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
@@ -302,13 +296,13 @@ void OverviewPage::setWalletModel(WalletModel *model)
void OverviewPage::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
ui->labelTransactionsStatus->setIcon(icon);
ui->labelWalletStatus->setIcon(icon);
}
-#endif
+
+ QWidget::changeEvent(e);
}
void OverviewPage::updateDisplayUnit()
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 755a107a00..5270741c0d 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -65,7 +65,6 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
- void handleOutOfSyncWarningClicks();
void setMonospacedFont(bool use_embedded_font);
};
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index b324693692..1b7fda6e77 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -179,5 +179,7 @@ void PeerTableModel::refresh()
m_peers_data.swap(new_peers_data);
}
- Q_EMIT changed();
+ const auto top_left = index(0, 0);
+ const auto bottom_right = index(rowCount() - 1, columnCount() - 1);
+ Q_EMIT dataChanged(top_left, bottom_right);
}
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 0ff1b5dba7..0d841ebf28 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -73,9 +73,6 @@ public:
public Q_SLOTS:
void refresh();
-Q_SIGNALS:
- void changed();
-
private:
//! Internal peer data structure.
QList<CNodeCombinedStats> m_peers_data{};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 99318c3bc0..2adfeeaaf0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -50,7 +50,7 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
bool complete;
size_t n_could_sign;
FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to load transaction: %1")
.arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
@@ -67,7 +67,7 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to sign transaction: %1")
@@ -226,7 +226,7 @@ void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
size_t n_signed;
bool complete;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
return 0;
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index f5200bb5c0..7cdd568644 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -27,8 +27,8 @@ QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(nullptr)
{
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Save Image…"), this, &QRImageWidget::saveImage);
- contextMenu->addAction(tr("Copy Image"), this, &QRImageWidget::copyImage);
+ contextMenu->addAction(tr("&Save Image…"), this, &QRImageWidget::saveImage);
+ contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 3f4d7f85e6..d47ee95826 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -44,11 +44,11 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy URI"), this, &ReceiveCoinsDialog::copyURI);
- contextMenu->addAction(tr("Copy address"), this, &ReceiveCoinsDialog::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &ReceiveCoinsDialog::copyLabel);
- copyMessageAction = contextMenu->addAction(tr("Copy message"), this, &ReceiveCoinsDialog::copyMessage);
- copyAmountAction = contextMenu->addAction(tr("Copy amount"), this, &ReceiveCoinsDialog::copyAmount);
+ contextMenu->addAction(tr("Copy &URI"), this, &ReceiveCoinsDialog::copyURI);
+ contextMenu->addAction(tr("&Copy address"), this, &ReceiveCoinsDialog::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &ReceiveCoinsDialog::copyLabel);
+ copyMessageAction = contextMenu->addAction(tr("Copy &message"), this, &ReceiveCoinsDialog::copyMessage);
+ copyAmountAction = contextMenu->addAction(tr("Copy &amount"), this, &ReceiveCoinsDialog::copyAmount);
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index abe7de8f89..41f22e9c34 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -90,7 +90,7 @@ void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
ui->wallet_content->hide();
}
- ui->btnVerify->setVisible(this->model->wallet().hasExternalSigner());
+ ui->btnVerify->setVisible(model->wallet().hasExternalSigner());
connect(ui->btnVerify, &QPushButton::clicked, [this] {
model->displayAddress(info.address.toStdString());
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 9579e6dc24..9c57816f91 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -35,6 +35,7 @@
#endif
#include <QAbstractButton>
+#include <QAbstractItemModel>
#include <QDateTime>
#include <QFont>
#include <QKeyEvent>
@@ -529,6 +530,8 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
//: Secondary shortcut to decrease the RPC console font size.
GUIUtil::AddButtonShortcut(ui->fontSmallerButton, tr("Ctrl+_"));
+ ui->promptIcon->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/prompticon")));
+
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
@@ -675,16 +678,16 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create peer table context menu
peersTableContextMenu = new QMenu(this);
- peersTableContextMenu->addAction(tr("Disconnect"), this, &RPCConsole::disconnectSelectedNode);
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 hour"), [this] { banSelectedNode(60 * 60); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 day"), [this] { banSelectedNode(60 * 60 * 24); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
+ peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
// peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
- connect(model->getPeerTableModel(), &PeerTableModel::changed, this, &RPCConsole::updateDetailWidget);
+ connect(model->getPeerTableModel(), &QAbstractItemModel::dataChanged, [this] { updateDetailWidget(); });
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
@@ -701,7 +704,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create ban table context menu
banTableContextMenu = new QMenu(this);
- banTableContextMenu->addAction(tr("Unban"), this, &RPCConsole::unbanSelectedNode);
+ banTableContextMenu->addAction(tr("&Unban"), this, &RPCConsole::unbanSelectedNode);
connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
// ban table signal handling - clear peer details when clicking a peer in the ban table
@@ -882,7 +885,6 @@ void RPCConsole::keyPressEvent(QKeyEvent *event)
void RPCConsole::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
ui->clearButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontbigger")));
@@ -896,7 +898,8 @@ void RPCConsole::changeEvent(QEvent* e)
platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize * 2, consoleFontSize * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
}
-#endif
+
+ QWidget::changeEvent(e);
}
void RPCConsole::message(int category, const QString &message, bool html)
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index e87a2b97bc..c9bf757dfc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -200,12 +200,14 @@ void SendCoinsDialog::setModel(WalletModel *_model)
ui->optInRBF->setCheckState(Qt::Checked);
if (model->wallet().hasExternalSigner()) {
+ //: "device" usually means a hardware wallet
ui->sendButton->setText(tr("Sign on device"));
if (gArgs.GetArg("-signer", "") != "") {
ui->sendButton->setEnabled(true);
ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
} else {
ui->sendButton->setEnabled(false);
+ //: "External signer" means using devices such as hardware wallets.
ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
}
} else if (model->wallet().privateKeysDisabled()) {
@@ -414,23 +416,25 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
bool complete = false;
// Always fill without signing first. This prevents an external signer
// from being called prematurely and is not expensive.
- TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
assert(!complete);
assert(err == TransactionError::OK);
if (model->wallet().hasExternalSigner()) {
try {
- err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
} catch (const std::runtime_error& e) {
QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
send_failure = true;
return;
}
if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
+ //: "External signer" means using devices such as hardware wallets.
QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
send_failure = true;
return;
}
if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
+ //: "External signer" means using devices such as hardware wallets.
QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
send_failure = true;
return;
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index f701bb9615..683c0441fa 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -238,7 +238,6 @@ void SendCoinsEntry::updateDisplayUnit()
void SendCoinsEntry::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
ui->pasteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
@@ -246,7 +245,8 @@ void SendCoinsEntry::changeEvent(QEvent* e)
ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
}
-#endif
+
+ QStackedWidget::changeEvent(e);
}
bool SendCoinsEntry::updateLabel(const QString &address)
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index b982cc577d..33589f09bf 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -286,7 +286,6 @@ bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event)
void SignVerifyMessageDialog::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
@@ -297,5 +296,6 @@ void SignVerifyMessageDialog::changeEvent(QEvent* e)
ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/transaction_0")));
ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
}
-#endif
+
+ QDialog::changeEvent(e);
}
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index cb3dbd2267..9c31cd50df 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -85,11 +85,6 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- {
- LOCK(cs_main);
- UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
- g_chainman.Reset();
- }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index ea35f80cf5..e883337fb5 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -69,7 +69,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
->findChild<QCheckBox*>("optInRBF")
->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
uint256 txid;
- boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
+ boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](const uint256& hash, ChangeType status) {
if (status == CT_NEW) txid = hash;
}));
ConfirmSend();
@@ -146,14 +146,14 @@ void TestGUI(interfaces::Node& node)
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
- wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ QCOMPARE(result.last_scanned_block, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 1e8e012dcf..83d17a32c0 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -163,19 +163,19 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
- copyAddressAction = contextMenu->addAction(tr("Copy address"), this, &TransactionView::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &TransactionView::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &TransactionView::copyAmount);
- contextMenu->addAction(tr("Copy transaction ID"), this, &TransactionView::copyTxID);
- contextMenu->addAction(tr("Copy raw transaction"), this, &TransactionView::copyTxHex);
- contextMenu->addAction(tr("Copy full transaction details"), this, &TransactionView::copyTxPlainText);
- contextMenu->addAction(tr("Show transaction details"), this, &TransactionView::showDetails);
+ copyAddressAction = contextMenu->addAction(tr("&Copy address"), this, &TransactionView::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &TransactionView::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &TransactionView::copyAmount);
+ contextMenu->addAction(tr("Copy transaction &ID"), this, &TransactionView::copyTxID);
+ contextMenu->addAction(tr("Copy &raw transaction"), this, &TransactionView::copyTxHex);
+ contextMenu->addAction(tr("Copy full transaction &details"), this, &TransactionView::copyTxPlainText);
+ contextMenu->addAction(tr("&Show transaction details"), this, &TransactionView::showDetails);
contextMenu->addSeparator();
- bumpFeeAction = contextMenu->addAction(tr("Increase transaction fee"));
+ bumpFeeAction = contextMenu->addAction(tr("Increase transaction &fee"));
GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
bumpFeeAction->setObjectName("bumpFeeAction");
- abandonAction = contextMenu->addAction(tr("Abandon transaction"), this, &TransactionView::abandonTx);
- contextMenu->addAction(tr("Edit address label"), this, &TransactionView::editLabel);
+ abandonAction = contextMenu->addAction(tr("A&bandon transaction"), this, &TransactionView::abandonTx);
+ contextMenu->addAction(tr("&Edit address label"), this, &TransactionView::editLabel);
connect(dateWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseType);
@@ -245,7 +245,6 @@ void TransactionView::setModel(WalletModel *_model)
void TransactionView::changeEvent(QEvent* e)
{
-#ifdef Q_OS_MACOS
if (e->type() == QEvent::PaletteChange) {
watchOnlyWidget->setItemIcon(
TransactionFilterProxy::WatchOnlyFilter_Yes,
@@ -254,7 +253,8 @@ void TransactionView::changeEvent(QEvent* e)
TransactionFilterProxy::WatchOnlyFilter_No,
m_platform_style->SingleColorIcon(QStringLiteral(":/icons/eye_minus")));
}
-#endif
+
+ QWidget::changeEvent(e);
}
void TransactionView::chooseDate(int idx)
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 7e5790fd87..3cceb5ca5a 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -11,6 +11,7 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
+#include <external_signer.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
@@ -295,7 +296,6 @@ void CreateWalletActivity::create()
{
m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
-#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> signers;
try {
signers = node().externalSigners();
@@ -303,7 +303,6 @@ void CreateWalletActivity::create()
QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
}
m_create_wallet_dialog->setSigners(signers);
-#endif
m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
m_create_wallet_dialog->show();
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 0f2ea99685..a1f357e0db 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -242,8 +242,3 @@ WalletModel* WalletFrame::currentWalletModel() const
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
-
-void WalletFrame::outOfSyncWarningClicked()
-{
- Q_EMIT requestedSyncWarningInfo();
-}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 844ed121a0..4f77bd716f 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -47,9 +47,6 @@ public:
QSize sizeHint() const override { return m_size_hint; }
Q_SIGNALS:
- /** Notify that the user has requested more information about the out-of-sync warning */
- void requestedSyncWarningInfo();
-
void createWalletButtonClicked();
private:
@@ -98,8 +95,6 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
- /** Pass on signal over requested out-of-sync-warning information */
- void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e32b7c2807..967dd588b4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -525,7 +525,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index cc9e1502f0..3b8cf4c7ed 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -73,7 +73,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
- connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
+ connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
// Highlight transaction after send
@@ -347,8 +347,3 @@ void WalletView::showProgress(const QString &title, int nProgress)
}
}
}
-
-void WalletView::requestedSyncWarningInfo()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 68f8a5e95b..fedf06b710 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -111,9 +111,6 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
- /** User has requested more information about the out of sync state */
- void requestedSyncWarningInfo();
-
Q_SIGNALS:
void setPrivacy(bool privacy);
void transactionClicked();
diff --git a/src/rest.cpp b/src/rest.cpp
index 747c7aea19..d599f381e3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -125,7 +125,7 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
__FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
return nullptr;
}
- return node_context->chainman;
+ return node_context->chainman.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 83c1975d38..c4a89c9772 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -10,8 +10,11 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
+#include <deploymentinfo.h>
+#include <deploymentstatus.h>
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
@@ -37,6 +40,7 @@
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
+#include <versionbits.h>
#include <warnings.h>
#include <stdint.h>
@@ -84,7 +88,6 @@ ChainstateManager& EnsureChainman(const NodeContext& node)
if (!node.chainman) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
}
- WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman)));
return *node.chainman;
}
@@ -1344,32 +1347,29 @@ static RPCHelpMan verifychain()
};
}
-static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int softfork_height, int tip_height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep)
{
// For buried deployments.
- // A buried deployment is one where the height of the activation has been hardcoded into
- // the client implementation long after the consensus change has activated. See BIP 90.
- // Buried deployments with activation height value of
- // std::numeric_limits<int>::max() are disabled and thus hidden.
- if (softfork_height == std::numeric_limits<int>::max()) return;
+
+ if (!DeploymentEnabled(params, dep)) return;
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
// getblockchaininfo reports the softfork as active from when the chain height is
// one below the activation height
- rv.pushKV("active", tip_height + 1 >= softfork_height);
- rv.pushKV("height", softfork_height);
- softforks.pushKV(name, rv);
+ rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep));
+ rv.pushKV("height", params.DeploymentHeight(dep));
+ softforks.pushKV(DeploymentName(dep), rv);
}
-static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
// For BIP9 deployments.
- // Deployments that are never active are hidden.
- if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
+
+ if (!DeploymentEnabled(consensusParams, id)) return;
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = VersionBitsState(active_chain_tip, consensusParams, id, versionbitscache);
+ const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id);
switch (thresholdState) {
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
@@ -1383,12 +1383,12 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniVal
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = VersionBitsStateSinceHeight(active_chain_tip, consensusParams, id, versionbitscache);
+ int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = VersionBitsStatistics(active_chain_tip, consensusParams, id);
+ BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1406,7 +1406,7 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniVal
}
rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
- softforks.pushKV(name, rv);
+ softforks.pushKV(DeploymentName(id), rv);
}
RPCHelpMan getblockchaininfo()
@@ -1503,14 +1503,14 @@ RPCHelpMan getblockchaininfo()
const Consensus::Params& consensusParams = Params().GetConsensus();
UniValue softforks(UniValue::VOBJ);
- BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height, height);
- BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height, height);
- BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height, height);
- BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight, height);
- BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight, height);
- BIP9SoftForkDescPushBack(tip, softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
- BIP9SoftForkDescPushBack(tip, softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
- obj.pushKV("softforks", softforks);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CSV);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
+ SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
+ obj.pushKV("softforks", softforks);
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
@@ -1707,7 +1707,7 @@ static RPCHelpMan preciousblock()
}
BlockValidationState state;
- chainman.ActiveChainstate().PreciousBlock(state, Params(), pblockindex);
+ chainman.ActiveChainstate().PreciousBlock(state, pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1744,10 +1744,10 @@ static RPCHelpMan invalidateblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
- chainman.ActiveChainstate().InvalidateBlock(state, Params(), pblockindex);
+ chainman.ActiveChainstate().InvalidateBlock(state, pblockindex);
if (state.IsValid()) {
- chainman.ActiveChainstate().ActivateBestChain(state, Params());
+ chainman.ActiveChainstate().ActivateBestChain(state);
}
if (!state.IsValid()) {
@@ -1788,7 +1788,7 @@ static RPCHelpMan reconsiderblock()
}
BlockValidationState state;
- chainman.ActiveChainstate().ActivateBestChain(state, Params());
+ chainman.ActiveChainstate().ActivateBestChain(state);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -2259,6 +2259,7 @@ public:
if (g_scan_in_progress.exchange(true)) {
return false;
}
+ CHECK_NONFATAL(g_scan_progress == 0);
m_could_reserve = true;
return true;
}
@@ -2266,6 +2267,7 @@ public:
~CoinsViewScanReserver() {
if (m_could_reserve) {
g_scan_in_progress = false;
+ g_scan_progress = 0;
}
}
};
@@ -2382,7 +2384,6 @@ static RPCHelpMan scantxoutset()
std::vector<CTxOut> input_txos;
std::map<COutPoint, Coin> coins;
g_should_abort_scan = false;
- g_scan_progress = 0;
int64_t count = 0;
std::unique_ptr<CCoinsViewCursor> pcursor;
CBlockIndex* tip;
@@ -2392,7 +2393,7 @@ static RPCHelpMan scantxoutset()
LOCK(cs_main);
CChainState& active_chainstate = chainman.ActiveChainstate();
active_chainstate.ForceFlushStateToDisk();
- pcursor = std::unique_ptr<CCoinsViewCursor>(active_chainstate.CoinsDB().Cursor());
+ pcursor = active_chainstate.CoinsDB().Cursor();
CHECK_NONFATAL(pcursor);
tip = active_chainstate.m_chain.Tip();
CHECK_NONFATAL(tip);
@@ -2591,7 +2592,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
- pcursor = std::unique_ptr<CCoinsViewCursor>(chainstate.CoinsDB().Cursor());
+ pcursor = chainstate.CoinsDB().Cursor();
tip = chainstate.m_blockman.LookupBlockIndex(stats.hashBlock);
CHECK_NONFATAL(tip);
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 6826e6fd07..2762d78493 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -10,6 +10,8 @@
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
+#include <deploymentinfo.h>
+#include <deploymentstatus.h>
#include <key_io.h>
#include <miner.h>
#include <net.h>
@@ -34,7 +36,6 @@
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
-#include <versionbitsinfo.h>
#include <warnings.h>
#include <memory>
@@ -114,7 +115,6 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
{
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
@@ -147,7 +147,6 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{ // Don't keep cs_main locked
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
nHeight = chainman.ActiveChain().Height();
nHeightEnd = nHeight+nGenerate;
}
@@ -776,7 +775,7 @@ static RPCHelpMan getblocktemplate()
pblock->nNonce = 0;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
- const bool fPreSegWit = (pindexPrev->nHeight + 1 < consensusParams.SegwitHeight);
+ const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
@@ -842,7 +841,7 @@ static RPCHelpMan getblocktemplate()
UniValue vbavailable(UniValue::VOBJ);
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
- ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
+ ThresholdState state = g_versionbitscache.State(pindexPrev, consensusParams, pos);
switch (state) {
case ThresholdState::DEFINED:
case ThresholdState::FAILED:
@@ -850,7 +849,7 @@ static RPCHelpMan getblocktemplate()
break;
case ThresholdState::LOCKED_IN:
// Ensure bit is set in block version
- pblock->nVersion |= VersionBitsMask(consensusParams, pos);
+ pblock->nVersion |= g_versionbitscache.Mask(consensusParams, pos);
// FALL THROUGH to get vbavailable set...
case ThresholdState::STARTED:
{
@@ -859,7 +858,7 @@ static RPCHelpMan getblocktemplate()
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it in the [default] version
- pblock->nVersion &= ~VersionBitsMask(consensusParams, pos);
+ pblock->nVersion &= ~g_versionbitscache.Mask(consensusParams, pos);
}
}
break;
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index ab239fe79c..5178ce60e8 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -131,6 +131,9 @@ static RPCHelpMan createmultisig()
if (!ParseOutputType(request.params[2].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
}
+ if (output_type == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
+ }
}
// Construct using pay-to-script-hash:
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 414c6637a5..617dfec98f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -753,7 +753,8 @@ static RPCHelpMan signrawtransactionwithkey()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of:\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of:\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -893,8 +894,7 @@ static RPCHelpMan testmempoolaccept()
"\nThis checks if transactions violate the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
- {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.\n"
- " Length must be one for now.",
+ {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
{
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
@@ -1655,6 +1655,7 @@ static RPCHelpMan utxoupdatepsbt()
}
// Fill the inputs
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs.at(i);
@@ -1671,7 +1672,7 @@ static RPCHelpMan utxoupdatepsbt()
// Update script/keypath information using descriptor data.
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
// we don't actually care about those here, in fact.
- SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
+ SignPSBTInput(public_provider, psbtx, i, &txdata, /* sighash_type */ 1);
}
// Update script/keypath information using descriptor data.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 51cf8a7d62..682b55742a 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -166,7 +166,7 @@ public:
* write_cache is the cache to write keys to (if not nullptr)
* Caches are not exclusive but this is not tested. Currently we use them exclusively
*/
- virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) = 0;
+ virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
@@ -181,7 +181,7 @@ public:
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
/** Get the descriptor string form with the xpub at the last hardened derivation */
- virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const = 0;
+ virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const = 0;
/** Derive a private key, if private data is available in arg. */
virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
@@ -199,7 +199,7 @@ class OriginPubkeyProvider final : public PubkeyProvider
public:
OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
{
if (!m_provider->GetPubKey(pos, arg, key, info, read_cache, write_cache)) return false;
std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
@@ -216,10 +216,10 @@ public:
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
- bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
+ bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override
{
std::string sub;
- if (!m_provider->ToNormalizedString(arg, sub, priv)) return false;
+ if (!m_provider->ToNormalizedString(arg, sub, cache)) return false;
// If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider
// In that case, we need to strip out the leading square bracket and fingerprint from the substring,
// and append that to our own origin string.
@@ -244,8 +244,8 @@ class ConstPubkeyProvider final : public PubkeyProvider
bool m_xonly;
public:
- ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly = false) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override
+ ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {}
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
{
key = m_pubkey;
info.path.clear();
@@ -263,9 +263,8 @@ public:
ret = EncodeSecret(key);
return true;
}
- bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
+ bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override
{
- if (priv) return ToPrivateString(arg, ret);
ret = ToString();
return true;
}
@@ -288,9 +287,6 @@ class BIP32PubkeyProvider final : public PubkeyProvider
CExtPubKey m_root_extkey;
KeyPath m_path;
DeriveType m_derive;
- // Cache of the parent of the final derived pubkeys.
- // Primarily useful for situations when no read_cache is provided
- CExtPubKey m_cached_xpub;
bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const
{
@@ -305,11 +301,14 @@ class BIP32PubkeyProvider final : public PubkeyProvider
}
// Derives the last xprv
- bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv) const
+ bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv, CExtKey& last_hardened) const
{
if (!GetExtKey(arg, xprv)) return false;
for (auto entry : m_path) {
xprv.Derive(xprv, entry);
+ if (entry >> 31) {
+ last_hardened = xprv;
+ }
}
return true;
}
@@ -327,7 +326,7 @@ public:
BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
{
// Info of parent of the to be derived pubkey
KeyOriginInfo parent_info;
@@ -343,6 +342,7 @@ public:
// Derive keys or fetch them from cache
CExtPubKey final_extkey = m_root_extkey;
CExtPubKey parent_extkey = m_root_extkey;
+ CExtPubKey last_hardened_extkey;
bool der = true;
if (read_cache) {
if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, final_extkey)) {
@@ -352,16 +352,17 @@ public:
final_extkey = parent_extkey;
if (m_derive == DeriveType::UNHARDENED) der = parent_extkey.Derive(final_extkey, pos);
}
- } else if (m_cached_xpub.pubkey.IsValid() && m_derive != DeriveType::HARDENED) {
- parent_extkey = final_extkey = m_cached_xpub;
- if (m_derive == DeriveType::UNHARDENED) der = parent_extkey.Derive(final_extkey, pos);
} else if (IsHardened()) {
CExtKey xprv;
- if (!GetDerivedExtKey(arg, xprv)) return false;
+ CExtKey lh_xprv;
+ if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false;
parent_extkey = xprv.Neuter();
if (m_derive == DeriveType::UNHARDENED) der = xprv.Derive(xprv, pos);
if (m_derive == DeriveType::HARDENED) der = xprv.Derive(xprv, pos | 0x80000000UL);
final_extkey = xprv.Neuter();
+ if (lh_xprv.key.IsValid()) {
+ last_hardened_extkey = lh_xprv.Neuter();
+ }
} else {
for (auto entry : m_path) {
der = parent_extkey.Derive(parent_extkey, entry);
@@ -376,15 +377,14 @@ public:
final_info_out = final_info_out_tmp;
key_out = final_extkey.pubkey;
- // We rely on the consumer to check that m_derive isn't HARDENED as above
- // But we can't have already cached something in case we read something from the cache
- // and parent_extkey isn't actually the parent.
- if (!m_cached_xpub.pubkey.IsValid()) m_cached_xpub = parent_extkey;
-
if (write_cache) {
// Only cache parent if there is any unhardened derivation
if (m_derive != DeriveType::HARDENED) {
write_cache->CacheParentExtPubKey(m_expr_index, parent_extkey);
+ // Cache last hardened xpub if we have it
+ if (last_hardened_extkey.pubkey.IsValid()) {
+ write_cache->CacheLastHardenedExtPubKey(m_expr_index, last_hardened_extkey);
+ }
} else if (final_info_out.path.size() > 0) {
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);
}
@@ -412,11 +412,10 @@ public:
}
return true;
}
- bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override
+ bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override
{
// For hardened derivation type, just return the typical string, nothing to normalize
if (m_derive == DeriveType::HARDENED) {
- if (priv) return ToPrivateString(arg, out);
out = ToString();
return true;
}
@@ -429,33 +428,42 @@ public:
}
// Either no derivation or all unhardened derivation
if (i == -1) {
- if (priv) return ToPrivateString(arg, out);
out = ToString();
return true;
}
- // Derive the xpub at the last hardened step
- CExtKey xprv;
- if (!GetExtKey(arg, xprv)) return false;
+ // Get the path to the last hardened stup
KeyOriginInfo origin;
int k = 0;
for (; k <= i; ++k) {
- // Derive
- xprv.Derive(xprv, m_path.at(k));
// Add to the path
origin.path.push_back(m_path.at(k));
- // First derivation element, get the fingerprint for origin
- if (k == 0) {
- std::copy(xprv.vchFingerprint, xprv.vchFingerprint + 4, origin.fingerprint);
- }
}
// Build the remaining path
KeyPath end_path;
for (; k < (int)m_path.size(); ++k) {
end_path.push_back(m_path.at(k));
}
+ // Get the fingerprint
+ CKeyID id = m_root_extkey.pubkey.GetID();
+ std::copy(id.begin(), id.begin() + 4, origin.fingerprint);
+
+ CExtPubKey xpub;
+ CExtKey lh_xprv;
+ // If we have the cache, just get the parent xpub
+ if (cache != nullptr) {
+ cache->GetCachedLastHardenedExtPubKey(m_expr_index, xpub);
+ }
+ if (!xpub.pubkey.IsValid()) {
+ // Cache miss, or nor cache, or need privkey
+ CExtKey xprv;
+ if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false;
+ xpub = lh_xprv.Neuter();
+ }
+ assert(xpub.pubkey.IsValid());
+
// Build the string
std::string origin_str = HexStr(origin.fingerprint) + FormatHDKeypath(origin.path);
- out = "[" + origin_str + "]" + (priv ? EncodeExtKey(xprv) : EncodeExtPubKey(xprv.Neuter())) + FormatHDKeypath(end_path);
+ out = "[" + origin_str + "]" + EncodeExtPubKey(xpub) + FormatHDKeypath(end_path);
if (IsRange()) {
out += "/*";
assert(m_derive == DeriveType::UNHARDENED);
@@ -465,7 +473,8 @@ public:
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
{
CExtKey extkey;
- if (!GetDerivedExtKey(arg, extkey)) return false;
+ CExtKey dummy;
+ if (!GetDerivedExtKey(arg, extkey, dummy)) return false;
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
key = extkey.key;
@@ -508,6 +517,13 @@ public:
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {}
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::vector<std::unique_ptr<DescriptorImpl>> scripts, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(std::move(scripts)) {}
+ enum class StringType
+ {
+ PUBLIC,
+ PRIVATE,
+ NORMALIZED,
+ };
+
bool IsSolvable() const override
{
for (const auto& arg : m_subdescriptor_args) {
@@ -527,19 +543,19 @@ public:
return false;
}
- virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const
+ virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const
{
size_t pos = 0;
for (const auto& scriptarg : m_subdescriptor_args) {
if (pos++) ret += ",";
std::string tmp;
- if (!scriptarg->ToStringHelper(arg, tmp, priv, normalized)) return false;
+ if (!scriptarg->ToStringHelper(arg, tmp, type, cache)) return false;
ret += std::move(tmp);
}
return true;
}
- bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const
+ bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr) const
{
std::string extra = ToStringExtra();
size_t pos = extra.size() > 0 ? 1 : 0;
@@ -547,17 +563,21 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pos++) ret += ",";
std::string tmp;
- if (normalized) {
- if (!pubkey->ToNormalizedString(*arg, tmp, priv)) return false;
- } else if (priv) {
- if (!pubkey->ToPrivateString(*arg, tmp)) return false;
- } else {
- tmp = pubkey->ToString();
+ switch (type) {
+ case StringType::NORMALIZED:
+ if (!pubkey->ToNormalizedString(*arg, tmp, cache)) return false;
+ break;
+ case StringType::PRIVATE:
+ if (!pubkey->ToPrivateString(*arg, tmp)) return false;
+ break;
+ case StringType::PUBLIC:
+ tmp = pubkey->ToString();
+ break;
}
ret += std::move(tmp);
}
std::string subscript;
- if (!ToStringSubScriptHelper(arg, subscript, priv, normalized)) return false;
+ if (!ToStringSubScriptHelper(arg, subscript, type, cache)) return false;
if (pos && subscript.size()) ret += ',';
out = std::move(ret) + std::move(subscript) + ")";
return true;
@@ -566,20 +586,20 @@ public:
std::string ToString() const final
{
std::string ret;
- ToStringHelper(nullptr, ret, false, false);
+ ToStringHelper(nullptr, ret, StringType::PUBLIC);
return AddChecksum(ret);
}
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final
{
- bool ret = ToStringHelper(&arg, out, true, false);
+ bool ret = ToStringHelper(&arg, out, StringType::PRIVATE);
out = AddChecksum(out);
return ret;
}
- bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override final
+ bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override final
{
- bool ret = ToStringHelper(&arg, out, priv, true);
+ bool ret = ToStringHelper(&arg, out, StringType::NORMALIZED, cache);
out = AddChecksum(out);
return ret;
}
@@ -640,20 +660,6 @@ public:
std::optional<OutputType> GetOutputType() const override { return std::nullopt; }
};
-static std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) {
- if (std::holds_alternative<PKHash>(dest) ||
- std::holds_alternative<ScriptHash>(dest)) {
- return OutputType::LEGACY;
- }
- if (std::holds_alternative<WitnessV0KeyHash>(dest) ||
- std::holds_alternative<WitnessV0ScriptHash>(dest) ||
- std::holds_alternative<WitnessV1Taproot>(dest) ||
- std::holds_alternative<WitnessUnknown>(dest)) {
- return OutputType::BECH32;
- }
- return std::nullopt;
-}
-
/** A parsed addr(A) descriptor. */
class AddressDescriptor final : public DescriptorImpl
{
@@ -843,9 +849,11 @@ protected:
XOnlyPubKey xpk(keys[0]);
if (!xpk.IsFullyValid()) return {};
builder.Finalize(xpk);
- return Vector(GetScriptForDestination(builder.GetOutput()));
+ WitnessV1Taproot output = builder.GetOutput();
+ out.tr_spenddata[output].Merge(builder.GetSpendData());
+ return Vector(GetScriptForDestination(output));
}
- bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override
+ bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const override
{
if (m_depths.empty()) return true;
std::vector<bool> path;
@@ -856,7 +864,7 @@ protected:
path.push_back(false);
}
std::string tmp;
- if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false;
+ if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, type, cache)) return false;
ret += std::move(tmp);
while (!path.empty() && path.back()) {
if (path.size() > 1) ret += '}';
@@ -872,7 +880,7 @@ public:
{
assert(m_subdescriptor_args.size() == m_depths.size());
}
- std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
bool IsSingleType() const final { return true; }
};
@@ -929,7 +937,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
CPubKey pubkey(data);
if (pubkey.IsFullyValid()) {
if (permit_uncompressed || pubkey.IsCompressed()) {
- return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey);
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false);
} else {
error = "Uncompressed keys are not allowed";
return nullptr;
@@ -950,7 +958,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
if (permit_uncompressed || key.IsCompressed()) {
CPubKey pubkey = key.GetPubKey();
out.keys.emplace(pubkey.GetID(), key);
- return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey);
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, ctx == ParseScriptContext::P2TR);
} else {
error = "Uncompressed keys are not allowed";
return nullptr;
@@ -1219,7 +1227,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
{
- std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey);
+ std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, false);
KeyOriginInfo info;
if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
@@ -1227,18 +1235,42 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo
return key_provider;
}
+std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02};
+ std::copy(xkey.begin(), xkey.end(), full_key + 1);
+ 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)) {
+ 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;
+}
+
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
+ if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) {
+ XOnlyPubKey key{Span<const unsigned char>{script.data() + 1, script.data() + 33}};
+ return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider));
+ }
+
std::vector<std::vector<unsigned char>> data;
TxoutType txntype = Solver(script, data);
- if (txntype == TxoutType::PUBKEY) {
+ if (txntype == TxoutType::PUBKEY && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {
CPubKey pubkey(data[0]);
if (pubkey.IsValid()) {
return std::make_unique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TxoutType::PUBKEYHASH) {
+ if (txntype == TxoutType::PUBKEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1246,7 +1278,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return std::make_unique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TxoutType::WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_KEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1254,7 +1286,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return std::make_unique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TxoutType::MULTISIG) {
+ if (txntype == TxoutType::MULTISIG && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
CPubKey pubkey(data[i]);
@@ -1271,7 +1303,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (sub) return std::make_unique<SHDescriptor>(std::move(sub));
}
}
- if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) {
CScriptID scriptid;
CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
CScript subscript;
@@ -1280,6 +1312,40 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (sub) return std::make_unique<WSHDescriptor>(std::move(sub));
}
}
+ if (txntype == TxoutType::WITNESS_V1_TAPROOT && ctx == ParseScriptContext::TOP) {
+ // Extract x-only pubkey from output.
+ XOnlyPubKey pubkey;
+ std::copy(data[0].begin(), data[0].end(), pubkey.begin());
+ // Request spending data.
+ TaprootSpendData tap;
+ if (provider.GetTaprootSpendData(pubkey, tap)) {
+ // If found, convert it back to tree form.
+ auto tree = InferTaprootTree(tap, pubkey);
+ if (tree) {
+ // If that works, try to infer subdescriptors for all leaves.
+ bool ok = true;
+ std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
+ std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
+ for (const auto& [depth, script, leaf_ver] : *tree) {
+ std::unique_ptr<DescriptorImpl> subdesc;
+ if (leaf_ver == TAPROOT_LEAF_TAPSCRIPT) {
+ subdesc = InferScript(script, ParseScriptContext::P2TR, provider);
+ }
+ if (!subdesc) {
+ ok = false;
+ break;
+ } else {
+ subscripts.push_back(std::move(subdesc));
+ depths.push_back(depth);
+ }
+ }
+ if (ok) {
+ auto key = InferXOnlyPubkey(tap.internal_key, ParseScriptContext::P2TR, provider);
+ return std::make_unique<TRDescriptor>(std::move(key), std::move(subscripts), std::move(depths));
+ }
+ }
+ }
+ }
CTxDestination dest;
if (ExtractDestination(script, dest)) {
@@ -1365,6 +1431,11 @@ void DescriptorCache::CacheDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_i
xpubs[der_index] = xpub;
}
+void DescriptorCache::CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub)
+{
+ m_last_hardened_xpubs[key_exp_pos] = xpub;
+}
+
bool DescriptorCache::GetCachedParentExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const
{
const auto& it = m_parent_xpubs.find(key_exp_pos);
@@ -1383,6 +1454,55 @@ bool DescriptorCache::GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t d
return true;
}
+bool DescriptorCache::GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const
+{
+ const auto& it = m_last_hardened_xpubs.find(key_exp_pos);
+ if (it == m_last_hardened_xpubs.end()) return false;
+ xpub = it->second;
+ return true;
+}
+
+DescriptorCache DescriptorCache::MergeAndDiff(const DescriptorCache& other)
+{
+ DescriptorCache diff;
+ for (const auto& parent_xpub_pair : other.GetCachedParentExtPubKeys()) {
+ CExtPubKey xpub;
+ if (GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) {
+ if (xpub != parent_xpub_pair.second) {
+ throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub");
+ }
+ continue;
+ }
+ CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second);
+ diff.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second);
+ }
+ for (const auto& derived_xpub_map_pair : other.GetCachedDerivedExtPubKeys()) {
+ for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
+ CExtPubKey xpub;
+ if (GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) {
+ if (xpub != derived_xpub_pair.second) {
+ throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub");
+ }
+ continue;
+ }
+ CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second);
+ diff.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second);
+ }
+ }
+ for (const auto& lh_xpub_pair : other.GetCachedLastHardenedExtPubKeys()) {
+ CExtPubKey xpub;
+ if (GetCachedLastHardenedExtPubKey(lh_xpub_pair.first, xpub)) {
+ if (xpub != lh_xpub_pair.second) {
+ throw std::runtime_error(std::string(__func__) + ": New cached last hardened xpub does not match already cached last hardened xpub");
+ }
+ continue;
+ }
+ CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second);
+ diff.CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second);
+ }
+ return diff;
+}
+
const ExtPubKeyMap DescriptorCache::GetCachedParentExtPubKeys() const
{
return m_parent_xpubs;
@@ -1392,3 +1512,8 @@ const std::unordered_map<uint32_t, ExtPubKeyMap> DescriptorCache::GetCachedDeriv
{
return m_derived_xpubs;
}
+
+const ExtPubKeyMap DescriptorCache::GetCachedLastHardenedExtPubKeys() const
+{
+ return m_last_hardened_xpubs;
+}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 332ae2f230..ecd7c4eea5 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -22,6 +22,8 @@ private:
std::unordered_map<uint32_t, ExtPubKeyMap> m_derived_xpubs;
/** Map key expression index -> parent xpub */
ExtPubKeyMap m_parent_xpubs;
+ /** Map key expression index -> last hardened xpub */
+ ExtPubKeyMap m_last_hardened_xpubs;
public:
/** Cache a parent xpub
@@ -50,11 +52,30 @@ public:
* @param[in] xpub The CExtPubKey to get from cache
*/
bool GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_index, CExtPubKey& xpub) const;
+ /** Cache a last hardened xpub
+ *
+ * @param[in] key_exp_pos Position of the key expression within the descriptor
+ * @param[in] xpub The CExtPubKey to cache
+ */
+ void CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub);
+ /** Retrieve a cached last hardened xpub
+ *
+ * @param[in] key_exp_pos Position of the key expression within the descriptor
+ * @param[in] xpub The CExtPubKey to get from cache
+ */
+ bool GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const;
/** Retrieve all cached parent xpubs */
const ExtPubKeyMap GetCachedParentExtPubKeys() const;
/** Retrieve all cached derived xpubs */
const std::unordered_map<uint32_t, ExtPubKeyMap> GetCachedDerivedExtPubKeys() const;
+ /** Retrieve all cached last hardened xpubs */
+ const ExtPubKeyMap GetCachedLastHardenedExtPubKeys() const;
+
+ /** Combine another DescriptorCache into this one.
+ * Returns a cache containing the items from the other cache unknown to current cache
+ */
+ DescriptorCache MergeAndDiff(const DescriptorCache& other);
};
/** \brief Interface for parsed descriptor objects.
@@ -94,7 +115,7 @@ struct Descriptor {
virtual bool ToPrivateString(const SigningProvider& provider, std::string& out) const = 0;
/** Convert the descriptor to a normalized string. Normalized descriptors have the xpub at the last hardened step. This fails if the provided provider does not have the private keys to derive that xpub. */
- virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, bool priv) const = 0;
+ virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const = 0;
/** Expand a descriptor at a specified position.
*
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 3c3c3ac1a8..ef48f89965 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1420,7 +1420,7 @@ uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
} // namespace
template <class T>
-void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
+void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs, bool force)
{
assert(!m_spent_outputs_ready);
@@ -1431,9 +1431,9 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
}
// Determine which precomputation-impacting features this transaction uses.
- bool uses_bip143_segwit = false;
- bool uses_bip341_taproot = false;
- for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
+ bool uses_bip143_segwit = force;
+ bool uses_bip341_taproot = force;
+ for (size_t inpos = 0; inpos < txTo.vin.size() && !(uses_bip143_segwit && uses_bip341_taproot); ++inpos) {
if (!txTo.vin[inpos].scriptWitness.IsNull()) {
if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
@@ -1478,8 +1478,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
}
// explicit instantiation
-template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
@@ -1711,7 +1711,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
uint256 sighash;
- assert(this->txdata);
+ if (!this->txdata) return HandleMissingData(m_mdb);
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
@@ -1740,9 +1740,9 @@ bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLoc
if (nLockTime > (int64_t)txTo->nLockTime)
return false;
- // Finally the nLockTime feature can be disabled and thus
- // CHECKLOCKTIMEVERIFY bypassed if every txin has been
- // finalized by setting nSequence to maxint. The
+ // Finally the nLockTime feature can be disabled in IsFinalTx()
+ // and thus CHECKLOCKTIMEVERIFY bypassed if every txin has
+ // been finalized by setting nSequence to maxint. The
// transaction would be allowed into the blockchain, making
// the opcode ineffective.
//
@@ -1847,16 +1847,14 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
return true;
}
-static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
+uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script)
+{
+ return (CHashWriter(HASHER_TAPLEAF) << leaf_version << script).GetSHA256();
+}
+
+uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash)
{
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
- //! The internal pubkey (x-only, so no Y coordinate parity).
- const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
- //! The output pubkey (taken from the scriptPubKey).
- const XOnlyPubKey q{uint256(program)};
- // Compute the tapleaf hash.
- tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
- // Compute the Merkle root from the leaf and the provided path.
uint256 k = tapleaf_hash;
for (int i = 0; i < path_len; ++i) {
CHashWriter ss_branch{HASHER_TAPBRANCH};
@@ -1868,8 +1866,21 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
+ return k;
+}
+
+static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash)
+{
+ assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
+ assert(program.size() >= uint256::size());
+ //! The internal pubkey (x-only, so no Y coordinate parity).
+ const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
+ //! The output pubkey (taken from the scriptPubKey).
+ const XOnlyPubKey q{uint256(program)};
+ // Compute the Merkle root from the leaf and the provided path.
+ const uint256 merkle_root = ComputeTaprootMerkleRoot(control, tapleaf_hash);
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
- return q.CheckTapTweak(p, k, control[0] & 1);
+ return q.CheckTapTweak(p, merkle_root, control[0] & 1);
}
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
@@ -1929,7 +1940,8 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) {
return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
}
- if (!VerifyTaprootCommitment(control, program, exec_script, execdata.m_tapleaf_hash)) {
+ execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, exec_script);
+ if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
execdata.m_tapleaf_hash_init = true;
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index fa4ee83e04..034c937b99 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -168,7 +168,7 @@ struct PrecomputedTransactionData
PrecomputedTransactionData() = default;
template <class T>
- void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
+ void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
@@ -260,6 +260,9 @@ enum class MissingDataBehavior
FAIL, //!< Just act as if the signature was invalid
};
+template<typename T>
+bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
+
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
@@ -314,6 +317,12 @@ public:
}
};
+/** Compute the BIP341 tapleaf hash from leaf version & script. */
+uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script);
+/** Compute the BIP341 taproot script tree Merkle root from control block and leaf hash.
+ * Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
+uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash);
+
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index da0092f9e3..65276f641f 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -11,13 +11,28 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/vector.h>
typedef std::vector<unsigned char> valtype;
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL),
+ m_txdata(nullptr)
+{
+}
+
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn),
+ checker(txdata ? MutableTransactionSignatureChecker(txTo, nIn, amount, *txdata, MissingDataBehavior::FAIL) :
+ MutableTransactionSignatureChecker(txTo, nIn, amount, MissingDataBehavior::FAIL)),
+ m_txdata(txdata)
+{
+}
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
+
CKey key;
if (!provider.GetKey(address, key))
return false;
@@ -26,13 +41,61 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
- // Signing for witness scripts needs the amount.
- if (sigversion == SigVersion::WITNESS_V0 && amount < 0) return false;
+ // Signing without known amount does not work in witness scripts.
+ if (sigversion == SigVersion::WITNESS_V0 && !MoneyRange(amount)) return false;
+
+ // BASE/WITNESS_V0 signatures don't support explicit SIGHASH_DEFAULT, use SIGHASH_ALL instead.
+ const int hashtype = nHashType == SIGHASH_DEFAULT ? SIGHASH_ALL : nHashType;
- uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
+ uint256 hash = SignatureHash(scriptCode, *txTo, nIn, hashtype, amount, sigversion, m_txdata);
if (!key.Sign(hash, vchSig))
return false;
- vchSig.push_back((unsigned char)nHashType);
+ vchSig.push_back((unsigned char)hashtype);
+ return true;
+}
+
+bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
+{
+ assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
+
+ CKey key;
+ {
+ // For now, use the old full pubkey-based key derivation logic. As it 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;
+ }
+ }
+
+ // BIP341/BIP342 signing needs lots of precomputed transaction data. While some
+ // (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
+ // of data present, for now, only support signing when everything is provided.
+ if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return false;
+
+ ScriptExecutionData execdata;
+ execdata.m_annex_init = true;
+ execdata.m_annex_present = false; // Only support annex-less signing for now.
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ execdata.m_codeseparator_pos_init = true;
+ execdata.m_codeseparator_pos = 0xFFFFFFFF; // Only support non-OP_CODESEPARATOR BIP342 signing for now.
+ if (!leaf_hash) return false; // BIP342 signing needs leaf hash.
+ execdata.m_tapleaf_hash_init = true;
+ execdata.m_tapleaf_hash = *leaf_hash;
+ }
+ uint256 hash;
+ if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
+ sig.resize(64);
+ if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false;
+ if (nHashType) sig.push_back(nHashType);
return true;
}
@@ -92,6 +155,86 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
return false;
}
+static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
+{
+ auto lookup_key = std::make_pair(pubkey, leaf_hash);
+ auto it = sigdata.taproot_script_sigs.find(lookup_key);
+ if (it != sigdata.taproot_script_sigs.end()) {
+ sig_out = it->second;
+ }
+ if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
+ sigdata.taproot_script_sigs[lookup_key] = sig_out;
+ return true;
+ }
+ return false;
+}
+
+static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, const CScript& script, std::vector<valtype>& result)
+{
+ // Only BIP342 tapscript signing is supported for now.
+ if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
+ SigVersion sigversion = SigVersion::TAPSCRIPT;
+
+ uint256 leaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(leaf_version) << script).GetSHA256();
+
+ // <xonly pubkey> OP_CHECKSIG
+ if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
+ XOnlyPubKey pubkey(MakeSpan(script).subspan(1, 32));
+ std::vector<unsigned char> sig;
+ if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
+ result = Vector(std::move(sig));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
+{
+ TaprootSpendData spenddata;
+
+ // Gather information about this output.
+ if (provider.GetTaprootSpendData(output, spenddata)) {
+ sigdata.tr_spenddata.Merge(spenddata);
+ }
+
+ // Try key path spending.
+ {
+ std::vector<unsigned char> sig;
+ if (sigdata.taproot_key_path_sig.size() == 0) {
+ if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
+ sigdata.taproot_key_path_sig = sig;
+ }
+ }
+ if (sigdata.taproot_key_path_sig.size()) {
+ result = Vector(sigdata.taproot_key_path_sig);
+ return true;
+ }
+ }
+
+ // Try script path spending.
+ std::vector<std::vector<unsigned char>> smallest_result_stack;
+ for (const auto& [key, control_blocks] : sigdata.tr_spenddata.scripts) {
+ const auto& [script, leaf_ver] = key;
+ std::vector<std::vector<unsigned char>> result_stack;
+ if (SignTaprootScript(provider, creator, sigdata, leaf_ver, script, result_stack)) {
+ result_stack.emplace_back(std::begin(script), std::end(script)); // Push the script
+ result_stack.push_back(*control_blocks.begin()); // Push the smallest control block
+ if (smallest_result_stack.size() == 0 ||
+ GetSerializeSize(result_stack, PROTOCOL_VERSION) < GetSerializeSize(smallest_result_stack, PROTOCOL_VERSION)) {
+ smallest_result_stack = std::move(result_stack);
+ }
+ }
+ }
+ if (smallest_result_stack.size() != 0) {
+ result = std::move(smallest_result_stack);
+ return true;
+ }
+
+ return false;
+}
+
/**
* Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
@@ -113,7 +256,6 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
- case TxoutType::WITNESS_V1_TAPROOT:
return false;
case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
@@ -175,6 +317,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
// Could not find witnessScript, add to missing
sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
+
+ case TxoutType::WITNESS_V1_TAPROOT:
+ return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -205,7 +350,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
- sigdata.scriptWitness.stack.clear();
if (solved && whichType == TxoutType::SCRIPTHASH)
{
@@ -238,10 +382,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
+ } else if (whichType == TxoutType::WITNESS_V1_TAPROOT && !P2SH) {
+ sigdata.witness = true;
+ if (solved) {
+ sigdata.scriptWitness.stack = std::move(result);
+ }
+ result.clear();
} else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
+ if (!sigdata.witness) sigdata.scriptWitness.stack.clear();
if (P2SH) {
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
}
@@ -402,6 +553,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
public:
DummySignatureChecker() {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
const DummySignatureChecker DUMMY_CHECKER;
@@ -427,6 +579,11 @@ public:
vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
return true;
}
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* tweak, SigVersion sigversion) const override
+ {
+ sig.assign(64, '\000');
+ return true;
+ }
};
}
@@ -476,6 +633,26 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mtx);
+
+ PrecomputedTransactionData txdata;
+ std::vector<CTxOut> spent_outputs;
+ spent_outputs.resize(mtx.vin.size());
+ bool have_all_spent_outputs = true;
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ have_all_spent_outputs = false;
+ } else {
+ spent_outputs[i] = CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey);
+ }
+ }
+ if (have_all_spent_outputs) {
+ txdata.Init(txConst, std::move(spent_outputs), true);
+ } else {
+ txdata.Init(txConst, {}, true);
+ }
+
// Sign what we can:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
@@ -490,7 +667,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
@@ -502,7 +679,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
}
ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, MissingDataBehavior::FAIL), &serror)) {
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
diff --git a/src/script/sign.h b/src/script/sign.h
index a1cfe1574d..b4e7318892 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -11,13 +11,13 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
+#include <script/standard.h>
#include <span.h>
#include <streams.h>
class CKey;
class CKeyID;
class CScript;
-class CScriptID;
class CTransaction;
class SigningProvider;
@@ -31,6 +31,7 @@ public:
/** Create a singular (non-script) signature. */
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
+ virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
};
/** A signature creator for transactions. */
@@ -40,11 +41,14 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
int nHashType;
CAmount amount;
const MutableTransactionSignatureChecker checker;
+ const PrecomputedTransactionData* m_txdata;
public:
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
+ MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
};
/** A signature creator that just produces 71-byte empty signatures. */
@@ -64,8 +68,11 @@ struct SignatureData {
CScript redeem_script; ///< The redeemScript (if any) for the input
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
+ TaprootSpendData tr_spenddata; ///< Taproot spending data.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
+ std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
+ std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 9781ec32af..b80fbe22ce 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -44,6 +44,11 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
return m_provider->GetKeyOrigin(keyid, info);
}
+bool HidingSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return m_provider->GetTaprootSpendData(output_key, spenddata);
+}
+
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
@@ -54,6 +59,10 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info)
return ret;
}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return LookupHelper(tr_spenddata, output_key, spenddata);
+}
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
{
@@ -66,6 +75,10 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
+ ret.tr_spenddata = a.tr_spenddata;
+ for (const auto& [output_key, spenddata] : b.tr_spenddata) {
+ ret.tr_spenddata[output_key].Merge(spenddata);
+ }
return ret;
}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 76f31d2f6f..939ae10622 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -25,6 +25,7 @@ public:
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
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; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
@@ -42,6 +43,7 @@ public:
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
struct FlatSigningProvider final : public SigningProvider
@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
+ std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index a4b11cc0a9..b8349bb9ab 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -377,6 +377,16 @@ bool IsValidDestination(const CTxDestination& dest) {
/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b)
{
NodeInfo ret;
+ /* Iterate over all tracked leaves in a, add b's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : a.leaves) {
+ leaf.merkle_branch.push_back(b.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
+ /* Iterate over all tracked leaves in b, add a's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : b.leaves) {
+ leaf.merkle_branch.push_back(a.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
/* Lexicographically sort a and b's hash, and compute parent hash. */
if (a.hash < b.hash) {
ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
@@ -386,6 +396,27 @@ bool IsValidDestination(const CTxDestination& dest) {
return ret;
}
+void TaprootSpendData::Merge(TaprootSpendData other)
+{
+ // TODO: figure out how to better deal with conflicting information
+ // being merged.
+ if (internal_key.IsNull() && !other.internal_key.IsNull()) {
+ internal_key = other.internal_key;
+ }
+ if (merkle_root.IsNull() && !other.merkle_root.IsNull()) {
+ merkle_root = other.merkle_root;
+ }
+ for (auto& [key, control_blocks] : other.scripts) {
+ // Once P0083R3 is supported by all our targeted platforms,
+ // this loop body can be replaced with:
+ // scripts[key].merge(std::move(control_blocks));
+ auto& target = scripts[key];
+ for (auto& control_block: control_blocks) {
+ target.insert(std::move(control_block));
+ }
+ }
+}
+
void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
{
assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT);
@@ -435,13 +466,14 @@ void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
return branch.size() == 0 || (branch.size() == 1 && branch[0]);
}
-TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version, bool track)
{
assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
if (!IsValid()) return *this;
- /* Construct NodeInfo object with leaf hash. */
+ /* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
NodeInfo node;
node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ if (track) node.leaves.emplace_back(LeafInfo{script, leaf_version, {}});
/* Insert into the branch. */
Insert(std::move(node), depth);
return *this;
@@ -464,8 +496,168 @@ TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key)
m_internal_key = internal_key;
auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash);
assert(ret.has_value());
- std::tie(m_output_key, std::ignore) = *ret;
+ std::tie(m_output_key, m_parity) = *ret;
return *this;
}
WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; }
+
+TaprootSpendData TaprootBuilder::GetSpendData() const
+{
+ TaprootSpendData spd;
+ spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
+ spd.internal_key = m_internal_key;
+ if (m_branch.size()) {
+ // If any script paths exist, they have been combined into the root m_branch[0]
+ // by now. Compute the control block for each of its tracked leaves, and put them in
+ // spd.scripts.
+ for (const auto& leaf : m_branch[0]->leaves) {
+ std::vector<unsigned char> control_block;
+ control_block.resize(TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size());
+ control_block[0] = leaf.leaf_version | (m_parity ? 1 : 0);
+ std::copy(m_internal_key.begin(), m_internal_key.end(), control_block.begin() + 1);
+ if (leaf.merkle_branch.size()) {
+ std::copy(leaf.merkle_branch[0].begin(),
+ leaf.merkle_branch[0].begin() + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size(),
+ control_block.begin() + TAPROOT_CONTROL_BASE_SIZE);
+ }
+ spd.scripts[{leaf.script, leaf.leaf_version}].insert(std::move(control_block));
+ }
+ }
+ return spd;
+}
+
+std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output)
+{
+ // Verify that the output matches the assumed Merkle root and internal key.
+ auto tweak = spenddata.internal_key.CreateTapTweak(spenddata.merkle_root.IsNull() ? nullptr : &spenddata.merkle_root);
+ if (!tweak || tweak->first != output) return std::nullopt;
+ // If the Merkle root is 0, the tree is empty, and we're done.
+ std::vector<std::tuple<int, CScript, int>> ret;
+ if (spenddata.merkle_root.IsNull()) return ret;
+
+ /** Data structure to represent the nodes of the tree we're going to build. */
+ struct TreeNode {
+ /** Hash of this node, if known; 0 otherwise. */
+ uint256 hash;
+ /** The left and right subtrees (note that their order is irrelevant). */
+ std::unique_ptr<TreeNode> sub[2];
+ /** If this is known to be a leaf node, a pointer to the (script, leaf_ver) pair.
+ * nullptr otherwise. */
+ const std::pair<CScript, int>* leaf = nullptr;
+ /** Whether or not this node has been explored (is known to be a leaf, or known to have children). */
+ bool explored = false;
+ /** Whether or not this node is an inner node (unknown until explored = true). */
+ bool inner;
+ /** Whether or not we have produced output for this subtree. */
+ bool done = false;
+ };
+
+ // Build tree from the provided branches.
+ TreeNode root;
+ root.hash = spenddata.merkle_root;
+ for (const auto& [key, control_blocks] : spenddata.scripts) {
+ const auto& [script, leaf_ver] = key;
+ for (const auto& control : control_blocks) {
+ // Skip script records with nonsensical leaf version.
+ if (leaf_ver < 0 || leaf_ver >= 0x100 || leaf_ver & 1) continue;
+ // Skip script records with invalid control block sizes.
+ if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE ||
+ ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) continue;
+ // Skip script records that don't match the control block.
+ if ((control[0] & TAPROOT_LEAF_MASK) != leaf_ver) continue;
+ // Skip script records that don't match the provided Merkle root.
+ const uint256 leaf_hash = ComputeTapleafHash(leaf_ver, script);
+ const uint256 merkle_root = ComputeTaprootMerkleRoot(control, leaf_hash);
+ if (merkle_root != spenddata.merkle_root) continue;
+
+ TreeNode* node = &root;
+ size_t levels = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
+ for (size_t depth = 0; depth < levels; ++depth) {
+ // Can't descend into a node which we already know is a leaf.
+ if (node->explored && !node->inner) return std::nullopt;
+
+ // Extract partner hash from Merkle branch in control block.
+ uint256 hash;
+ std::copy(control.begin() + TAPROOT_CONTROL_BASE_SIZE + (levels - 1 - depth) * TAPROOT_CONTROL_NODE_SIZE,
+ control.begin() + TAPROOT_CONTROL_BASE_SIZE + (levels - depth) * TAPROOT_CONTROL_NODE_SIZE,
+ hash.begin());
+
+ if (node->sub[0]) {
+ // Descend into the existing left or right branch.
+ bool desc = false;
+ for (int i = 0; i < 2; ++i) {
+ if (node->sub[i]->hash == hash || (node->sub[i]->hash.IsNull() && node->sub[1-i]->hash != hash)) {
+ node->sub[i]->hash = hash;
+ node = &*node->sub[1-i];
+ desc = true;
+ break;
+ }
+ }
+ if (!desc) return std::nullopt; // This probably requires a hash collision to hit.
+ } else {
+ // We're in an unexplored node. Create subtrees and descend.
+ node->explored = true;
+ node->inner = true;
+ node->sub[0] = std::make_unique<TreeNode>();
+ node->sub[1] = std::make_unique<TreeNode>();
+ node->sub[1]->hash = hash;
+ node = &*node->sub[0];
+ }
+ }
+ // Cannot turn a known inner node into a leaf.
+ if (node->sub[0]) return std::nullopt;
+ node->explored = true;
+ node->inner = false;
+ node->leaf = &key;
+ node->hash = leaf_hash;
+ }
+ }
+
+ // Recursive processing to turn the tree into flattened output. Use an explicit stack here to avoid
+ // overflowing the call stack (the tree may be 128 levels deep).
+ std::vector<TreeNode*> stack{&root};
+ while (!stack.empty()) {
+ TreeNode& node = *stack.back();
+ if (!node.explored) {
+ // Unexplored node, which means the tree is incomplete.
+ return std::nullopt;
+ } else if (!node.inner) {
+ // Leaf node; produce output.
+ ret.emplace_back(stack.size() - 1, node.leaf->first, node.leaf->second);
+ node.done = true;
+ stack.pop_back();
+ } else if (node.sub[0]->done && !node.sub[1]->done && !node.sub[1]->explored && !node.sub[1]->hash.IsNull() &&
+ (CHashWriter{HASHER_TAPBRANCH} << node.sub[1]->hash << node.sub[1]->hash).GetSHA256() == node.hash) {
+ // Whenever there are nodes with two identical subtrees under it, we run into a problem:
+ // the control blocks for the leaves underneath those will be identical as well, and thus
+ // they will all be matched to the same path in the tree. The result is that at the location
+ // where the duplicate occurred, the left child will contain a normal tree that can be explored
+ // and processed, but the right one will remain unexplored.
+ //
+ // This situation can be detected, by encountering an inner node with unexplored right subtree
+ // with known hash, and H_TapBranch(hash, hash) is equal to the parent node (this node)'s hash.
+ //
+ // To deal with this, simply process the left tree a second time (set its done flag to false;
+ // noting that the done flag of its children have already been set to false after processing
+ // those). To avoid ending up in an infinite loop, set the done flag of the right (unexplored)
+ // subtree to true.
+ node.sub[0]->done = false;
+ node.sub[1]->done = true;
+ } else if (node.sub[0]->done && node.sub[1]->done) {
+ // An internal node which we're finished with.
+ node.sub[0]->done = false;
+ node.sub[1]->done = false;
+ node.done = true;
+ stack.pop_back();
+ } else if (!node.sub[0]->done) {
+ // An internal node whose left branch hasn't been processed yet. Do so first.
+ stack.push_back(&*node.sub[0]);
+ } else if (!node.sub[1]->done) {
+ // An internal node whose right branch hasn't been processed yet. Do so first.
+ stack.push_back(&*node.sub[1]);
+ }
+ }
+
+ return ret;
+}
diff --git a/src/script/standard.h b/src/script/standard.h
index d7ea5cef27..ac4e2f3276 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -11,6 +11,7 @@
#include <uint256.h>
#include <util/hash_type.h>
+#include <map>
#include <string>
#include <variant>
@@ -209,15 +210,50 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
+struct ShortestVectorFirstComparator
+{
+ bool operator()(const std::vector<unsigned char>& a, const std::vector<unsigned char>& b) const
+ {
+ if (a.size() < b.size()) return true;
+ if (a.size() > b.size()) return false;
+ return a < b;
+ }
+};
+
+struct TaprootSpendData
+{
+ /** The BIP341 internal key. */
+ XOnlyPubKey internal_key;
+ /** The Merkle root of the script tree (0 if no scripts). */
+ uint256 merkle_root;
+ /** Map from (script, leaf_version) to (sets of) control blocks.
+ * The control blocks are sorted by size, so that the signing logic can
+ * easily prefer the cheapest one. */
+ std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
+ /** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
+ void Merge(TaprootSpendData other);
+};
+
/** Utility class to construct Taproot outputs from internal key and script tree. */
class TaprootBuilder
{
private:
+ /** Information about a tracked leaf in the Merkle tree. */
+ struct LeafInfo
+ {
+ CScript script; //!< The script.
+ int leaf_version; //!< The leaf version for that script.
+ std::vector<uint256> merkle_branch; //!< The hashing partners above this leaf.
+ };
+
/** Information associated with a node in the Merkle tree. */
struct NodeInfo
{
/** Merkle hash of this node. */
uint256 hash;
+ /** Tracked leaves underneath this node (either from the node itself, or its children).
+ * The merkle_branch field for each is the partners to get to *this* node. */
+ std::vector<LeafInfo> leaves;
};
/** Whether the builder is in a valid state so far. */
bool m_valid = true;
@@ -260,7 +296,8 @@ private:
std::vector<std::optional<NodeInfo>> m_branch;
XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing.
- XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
+ XOnlyPubKey m_output_key; //!< The output key, computed when finalizing.
+ bool m_parity; //!< The tweak parity, computed when finalizing.
/** Combine information about a parent Merkle tree node from its child nodes. */
static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b);
@@ -269,8 +306,9 @@ private:
public:
/** Add a new script at a certain depth in the tree. Add() operations must be called
- * in depth-first traversal order of binary tree. */
- TaprootBuilder& Add(int depth, const CScript& script, int leaf_version);
+ * in depth-first traversal order of binary tree. If track is true, it will be included in
+ * the GetSpendData() output. */
+ TaprootBuilder& Add(int depth, const CScript& script, int leaf_version, bool track = true);
/** Like Add(), but for a Merkle node with a given hash to the tree. */
TaprootBuilder& AddOmitted(int depth, const uint256& hash);
/** Finalize the construction. Can only be called when IsComplete() is true.
@@ -285,6 +323,16 @@ public:
WitnessV1Taproot GetOutput();
/** Check if a list of depths is legal (will lead to IsComplete()). */
static bool ValidDepths(const std::vector<int>& depths);
+ /** Compute spending data (after Finalize()). */
+ TaprootSpendData GetSpendData() const;
};
+/** Given a TaprootSpendData and the output key, reconstruct its script tree.
+ *
+ * If the output doesn't match the spenddata, or if the data in spenddata is incomplete,
+ * std::nullopt is returned. Otherwise, a vector of (depth, script, leaf_ver) tuples is
+ * returned, corresponding to a depth-first traversal of the script tree.
+ */
+std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output);
+
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 49b40924e0..f2eed956cd 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
+#include <i2p.h>
#include <test/data/asmap.raw.h>
#include <test/util/setup_common.h>
#include <util/asmap.h>
@@ -74,9 +75,9 @@ public:
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(const CService& addr)
{
- LOCK(cs);
int64_t nLastSuccess = 1;
- Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
+ // Set last good connection in the deep past.
+ Good(addr, true, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
@@ -783,6 +784,46 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.second != bucketAndEntry_asmap1_deser_addr2.second);
}
+BOOST_AUTO_TEST_CASE(remove_invalid)
+{
+ // Confirm that invalid addresses are ignored in unserialization.
+
+ CAddrManTest addrman;
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+
+ const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
+ const CAddress new2{ResolveService("6.6.6.6"), NODE_NONE};
+ const CAddress tried1{ResolveService("7.7.7.7"), NODE_NONE};
+ const CAddress tried2{ResolveService("8.8.8.8"), NODE_NONE};
+
+ addrman.Add({new1, tried1, new2, tried2}, CNetAddr{});
+ addrman.Good(tried1);
+ addrman.Good(tried2);
+ BOOST_REQUIRE_EQUAL(addrman.size(), 4);
+
+ stream << addrman;
+
+ const std::string str{stream.str()};
+ size_t pos;
+
+ const char new2_raw[]{6, 6, 6, 6};
+ const uint8_t new2_raw_replacement[]{0, 0, 0, 0}; // 0.0.0.0 is !IsValid()
+ pos = str.find(new2_raw, 0, sizeof(new2_raw));
+ BOOST_REQUIRE(pos != std::string::npos);
+ BOOST_REQUIRE(pos + sizeof(new2_raw_replacement) <= stream.size());
+ memcpy(stream.data() + pos, new2_raw_replacement, sizeof(new2_raw_replacement));
+
+ const char tried2_raw[]{8, 8, 8, 8};
+ const uint8_t tried2_raw_replacement[]{255, 255, 255, 255}; // 255.255.255.255 is !IsValid()
+ pos = str.find(tried2_raw, 0, sizeof(tried2_raw));
+ BOOST_REQUIRE(pos != std::string::npos);
+ BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
+ memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
+
+ addrman.Clear();
+ stream >> addrman;
+ BOOST_CHECK_EQUAL(addrman.size(), 2);
+}
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{
@@ -926,5 +967,121 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
}
+BOOST_AUTO_TEST_CASE(reset_i2p_ports)
+{
+ CAddrManTest addrman1;
+ CAddrManTest addrman2;
+ const uint32_t good_time{static_cast<uint32_t>(GetAdjustedTime())};
+ constexpr uint16_t port = 8333;
+
+ // Has its port changed, will be re-positioned within the same bucket in vvNew.
+ const CAddress i2p_new1{
+ ResolveService("72l3ucjkuscrbiiepoehuwqgknyzgo7zuix5ty4puwrkyhtmnsga.b32.i2p", port),
+ NODE_NONE,
+ good_time};
+
+ // Has its port changed, will not be re-positioned in vvNew because ports 0 and 10075 result in
+ // the same bucket position.
+ const CAddress i2p_new2{
+ ResolveService("gehtac45oaghz54ypyopim64mql7oad2bqclla74l6tfeolzmodq.b32.i2p", 10075),
+ NODE_NONE,
+ good_time};
+
+ // Remains unchanged, port is already as it should be.
+ const CAddress i2p_new3{
+ ResolveService("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p",
+ I2P_SAM31_PORT),
+ NODE_NONE,
+ good_time};
+
+ // Has its port changed, re-positioning in vvNew will cause i2p_new3 to be evicted.
+ const CAddress i2p_new4{
+ ResolveService("c4cbbkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p", port),
+ NODE_NONE,
+ good_time};
+
+ // Remains unchanged.
+ const CAddress ipv4_new{ResolveService("1.2.3.4", port), NODE_NONE, good_time};
+
+ // Has its port changed, will be re-positioned in vvTried.
+ const CAddress i2p_tried1{
+ ResolveService("h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p", port),
+ NODE_NONE,
+ good_time};
+
+ // Has its port changed, will not be re-positioned in vvTried because ports 0 and 10537
+ // result in the same position (bucket, i).
+ const CAddress i2p_tried2{
+ ResolveService("pjs7or2ctvteeo5tu4bwyrtydeuhqhvdprtujn4daxr75jpebjxa.b32.i2p", 10537),
+ NODE_NONE,
+ good_time};
+
+ // Remains unchanged, port is already as it should be.
+ const CAddress i2p_tried3{
+ ResolveService("hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p",
+ I2P_SAM31_PORT),
+ NODE_NONE,
+ good_time};
+
+ // Has its port changed, re-positioning in vvTried will cause i2p_tried3 to be moved to vvNew.
+ const CAddress i2p_tried4{
+ ResolveService("hna37nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p", port),
+ NODE_NONE,
+ good_time};
+
+ // Remains unchanged.
+ const CAddress ipv4_tried{ResolveService("2.3.4.5", port), NODE_NONE, good_time};
+
+ const CNetAddr source;
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+
+ addrman1.Add(i2p_new1, source);
+ addrman1.Add(i2p_new2, source);
+ addrman1.Add(i2p_new3, source);
+ addrman1.Add(i2p_new4, source);
+ addrman1.Add(ipv4_new, source);
+
+ addrman1.Add(i2p_tried1, source);
+ addrman1.Good(i2p_tried1);
+ addrman1.Add(i2p_tried2, source);
+ addrman1.Good(i2p_tried2);
+ addrman1.Add(i2p_tried3, source);
+ addrman1.Good(i2p_tried3);
+ addrman1.Add(i2p_tried4, source);
+ addrman1.Good(i2p_tried4);
+ addrman1.Add(ipv4_tried, source);
+ addrman1.Good(ipv4_tried);
+
+ stream << addrman1;
+ stream >> addrman2;
+
+ const size_t max_addresses{0};
+ const size_t max_pct{0};
+
+ auto addresses = addrman2.GetAddr(max_addresses, max_pct, NET_I2P);
+ BOOST_REQUIRE_EQUAL(addresses.size(), 7UL);
+ std::sort(addresses.begin(), addresses.end()); // Just some deterministic order.
+ BOOST_CHECK_EQUAL(addresses[0].ToStringIP(), i2p_new4.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[0].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[1].ToStringIP(), i2p_new2.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[1].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[2].ToStringIP(), i2p_tried4.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[2].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[3].ToStringIP(), i2p_tried3.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[3].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[4].ToStringIP(), i2p_tried1.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[4].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[5].ToStringIP(), i2p_tried2.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[5].GetPort(), I2P_SAM31_PORT);
+ BOOST_CHECK_EQUAL(addresses[6].ToStringIP(), i2p_new1.ToStringIP());
+ BOOST_CHECK_EQUAL(addresses[6].GetPort(), I2P_SAM31_PORT);
+
+ addresses = addrman2.GetAddr(max_addresses, max_pct, NET_IPV4);
+ BOOST_REQUIRE_EQUAL(addresses.size(), 2UL);
+ std::sort(addresses.begin(), addresses.end()); // Just some deterministic order.
+ BOOST_CHECK_EQUAL(addresses[0].ToStringIPPort(), ipv4_new.ToStringIPPort());
+ BOOST_CHECK_EQUAL(addresses[1].ToStringIPPort(), ipv4_tried.ToStringIPPort());
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 2f532ef598..2eb653e9ec 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(::ChainstateActive(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -117,9 +117,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
- for (const CBlockIndex* block_index = ::ChainActive().Genesis();
+ for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
@@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// BlockUntilSyncedToCurrentChain should return false before index is started.
BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(filter_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
@@ -145,9 +145,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
const CBlockIndex* block_index;
- for (block_index = ::ChainActive().Genesis();
+ for (block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
CheckFilterLookups(filter_index, block_index, last_header);
}
}
@@ -156,7 +156,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* tip;
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
CKey coinbase_key_A, coinbase_key_B;
coinbase_key_A.MakeNewKey(true);
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -210,7 +210,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -231,14 +231,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
@@ -250,7 +250,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 106fcd2a33..597d7a7340 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -22,7 +22,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = ChainActive().Tip();
+ block_index = m_node.chainman->ActiveChain().Tip();
}
// CoinStatsIndex should not be found before it is started.
@@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// is started.
BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(coin_stats_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(coin_stats_index.Start(m_node.chainman->ActiveChainstate()));
// Allow the CoinStatsIndex to catch up with the block index that is syncing
// in a background thread.
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* genesis_block_index;
{
LOCK(cs_main);
- genesis_block_index = ChainActive().Genesis();
+ genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
@@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
- new_block_index = ChainActive().Tip();
+ new_block_index = m_node.chainman->ActiveChain().Tip();
}
coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index a56ce51acb..5668ead1fb 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,36 +14,19 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
#include <txorphanage.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
-#include <test/util/setup_common.h>
-
#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-struct CConnmanTest : public CConnman {
- using CConnman::CConnman;
- void AddNode(CNode& node)
- {
- LOCK(cs_vNodes);
- vNodes.push_back(&node);
- }
- void ClearNodes()
- {
- LOCK(cs_vNodes);
- for (CNode* node : vNodes) {
- delete node;
- }
- vNodes.clear();
- }
-};
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -83,8 +66,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// This test requires that we have a chain with non-zero work.
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip() != nullptr);
- BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
}
// Test starts here
@@ -120,7 +103,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
@@ -130,13 +113,13 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
peerLogic.InitializeNode(&node);
node.fSuccessfullyConnected = true;
- connman->AddNode(node);
+ connman.AddTestNode(node);
}
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const CChainParams& chainparams = Params();
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -150,8 +133,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
std::vector<CNode *> vNodes;
// Mock some outbound peers
- for (int i=0; i<max_outbound_full_relay; ++i) {
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
}
peerLogic->CheckForStaleTipAndEvictPeers();
@@ -176,7 +159,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay; ++i) {
@@ -202,14 +185,14 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
- auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -233,7 +216,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
nodes[0]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[0]);
+ connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(nodes[0]->cs_sendProcessing);
@@ -249,7 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
nodes[1]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[1]);
+ connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(nodes[1]->cs_sendProcessing);
@@ -280,7 +263,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
nodes[2]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[2]);
+ connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(nodes[2]->cs_sendProcessing);
@@ -296,13 +279,13 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
const CChainParams& chainparams = Params();
- auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 36e2dac3ff..8553f80a17 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -124,14 +124,10 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
// Check that private can produce the normalized descriptors
std::string norm1;
- BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, false));
+ BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1));
BOOST_CHECK(EqualDescriptor(norm1, norm_pub));
- BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, false));
+ BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1));
BOOST_CHECK(EqualDescriptor(norm1, norm_pub));
- BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, true));
- BOOST_CHECK(EqualDescriptor(norm1, norm_prv));
- BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, true));
- BOOST_CHECK(EqualDescriptor(norm1, norm_prv));
// Check whether IsRange on both returns the expected result
BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0);
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 98ae32a8d0..db0b461873 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -57,15 +57,6 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.SelectTriedCollision();
},
[&] {
- (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
- },
- [&] {
- (void)addr_man.GetAddr(
- /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* network */ std::nullopt);
- },
- [&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
if (opt_address && opt_net_addr) {
@@ -109,12 +100,15 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
if (opt_service) {
addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
}
- },
- [&] {
- (void)addr_man.Check();
});
}
- (void)addr_man.size();
+ const CAddrMan& const_addr_man{addr_man};
+ (void)/*const_*/addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
+ (void)/*const_*/addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ (void)const_addr_man.size();
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
- data_stream << addr_man;
+ data_stream << const_addr_man;
}
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 759a70a857..182aabc79b 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -9,8 +9,10 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
+#include <util/readwritefile.h>
#include <util/system.h>
+#include <cassert>
#include <cstdint>
#include <limits>
#include <string>
@@ -30,6 +32,13 @@ void initialize_banman()
static const auto testing_setup = MakeNoLogFileContext<>();
}
+static bool operator==(const CBanEntry& lhs, const CBanEntry& rhs)
+{
+ return lhs.nVersion == rhs.nVersion &&
+ lhs.nCreateTime == rhs.nCreateTime &&
+ lhs.nBanUntil == rhs.nBanUntil;
+}
+
FUZZ_TARGET_INIT(banman, initialize_banman)
{
// The complexity is O(N^2), where N is the input size, because each call
@@ -38,10 +47,23 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
int limit_max_ops{300};
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- const fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist.dat";
- fs::remove(banlist_file);
+ fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist";
+
+ const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
+ bool force_read_and_write_to_err{false};
+ if (start_with_corrupted_banlist) {
+ const std::string sfx{fuzzed_data_provider.ConsumeBool() ? ".dat" : ".json"};
+ assert(WriteBinaryFile(banlist_file.string() + sfx,
+ fuzzed_data_provider.ConsumeRandomLengthString()));
+ } else {
+ force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
+ if (force_read_and_write_to_err) {
+ banlist_file = fs::path{"path"} / "to" / "inaccessible" / "fuzzed_banlist";
+ }
+ }
+
{
- BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)};
+ BanMan ban_man{banlist_file, /* client_interface */ nullptr, /* default_ban_time */ ConsumeBanTimeOffset(fuzzed_data_provider)};
while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
@@ -79,6 +101,17 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
});
}
+ if (!force_read_and_write_to_err) {
+ ban_man.DumpBanlist();
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+ banmap_t banmap;
+ ban_man.GetBanned(banmap);
+ BanMan ban_man_read{banlist_file, /* client_interface */ nullptr, /* default_ban_time */ 0};
+ banmap_t banmap_read;
+ ban_man_read.GetBanned(banmap_read);
+ assert(banmap == banmap_read);
+ }
}
- fs::remove(banlist_file);
+ fs::remove(banlist_file.string() + ".dat");
+ fs::remove(banlist_file.string() + ".json");
}
diff --git a/src/test/fuzz/base_encode_decode.cpp b/src/test/fuzz/base_encode_decode.cpp
index 4470e13a61..2b4f15115b 100644
--- a/src/test/fuzz/base_encode_decode.cpp
+++ b/src/test/fuzz/base_encode_decode.cpp
@@ -14,7 +14,12 @@
#include <string>
#include <vector>
-FUZZ_TARGET(base_encode_decode)
+void initialize_base_encode_decode()
+{
+ static const ECCVerifyHandle verify_handle;
+}
+
+FUZZ_TARGET_INIT(base_encode_decode, initialize_base_encode_decode)
{
const std::string random_encoded_string(buffer.begin(), buffer.end());
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b509ee0b26..f452696689 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -27,6 +27,7 @@
#include <vector>
namespace {
+const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin& a, const Coin& b)
@@ -39,6 +40,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -181,8 +183,8 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
}
{
- const CCoinsViewCursor* coins_view_cursor = backend_coins_view.Cursor();
- assert(coins_view_cursor == nullptr);
+ std::unique_ptr<CCoinsViewCursor> coins_view_cursor = backend_coins_view.Cursor();
+ assert(!coins_view_cursor);
(void)backend_coins_view.EstimateSize();
(void)backend_coins_view.GetBestBlock();
(void)backend_coins_view.GetHeadBlocks();
@@ -268,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index eeeac18968..f83747e424 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -19,6 +19,10 @@
FUZZ_TARGET(crypto)
{
+ // Hashing is expensive with sanitizers enabled, so limit the number of
+ // calls
+ int limit_max_ops{30};
+
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (data.empty()) {
@@ -36,7 +40,7 @@ FUZZ_TARGET(crypto)
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 1290c78712..721e4360d0 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -53,9 +53,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- CDataStream ds(SER_NETWORK, version);
+ CDataStream ds(ser_type, version);
ds << obj;
return ds;
}
@@ -69,9 +69,9 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt)
+void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt, const int ser_type = SER_NETWORK)
{
- CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION);
if (protocol_version) {
ds.SetVersion(*protocol_version);
} else {
@@ -92,9 +92,9 @@ void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optio
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- assert(Deserialize<T>(Serialize(obj, version)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj);
}
} // namespace
@@ -136,8 +136,7 @@ FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, {
FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, {
CPubKey pub_key;
DeserializeFromFuzzingInput(buffer, pub_key);
- // TODO: The following equivalence should hold for CPubKey? Fix.
- // AssertEqualAfterSerializeDeserialize(pub_key);
+ AssertEqualAfterSerializeDeserialize(pub_key);
})
FUZZ_TARGET_DESERIALIZE(script_deserialize, {
CScript script;
@@ -251,9 +250,37 @@ FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
DeserializeFromFuzzingInput(buffer, mh);
(void)mh.IsCommandValid();
})
-FUZZ_TARGET_DESERIALIZE(address_deserialize, {
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, {
CAddress a;
- DeserializeFromFuzzingInput(buffer, a);
+ DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
+ // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
+ // in all 5 formats (with/without nTime, v1/v2, network/disk)
+ AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_withtime, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
+ // A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v2, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
+ // with time if it's V1 compatible.
+ if (a.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ }
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(inv_deserialize, {
CInv i;
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index e9fa343896..e28e2feb0a 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -16,8 +16,6 @@
#include <pow.h>
#include <protocol.h>
#include <pubkey.h>
-#include <rpc/util.h>
-#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
@@ -158,20 +156,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
const CKeyID key_id{u160};
const CScriptID script_id{u160};
- // CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown
- const PKHash pk_hash{u160};
- const ScriptHash script_hash{u160};
- const WitnessV0KeyHash witness_v0_key_hash{u160};
- const WitnessV0ScriptHash witness_v0_script_hash{u256};
- const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash};
- const SigningProvider store;
- for (const CTxDestination& destination : destinations) {
- (void)DescribeAddress(destination);
- (void)EncodeDestination(destination);
- (void)GetKeyForDestination(store, destination);
- (void)GetScriptForDestination(destination);
- (void)IsValidDestination(destination);
- }
{
CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp
index 665ca01fa1..f58bf8b316 100644
--- a/src/test/fuzz/key_io.cpp
+++ b/src/test/fuzz/key_io.cpp
@@ -4,9 +4,6 @@
#include <chainparams.h>
#include <key_io.h>
-#include <rpc/util.h>
-#include <script/signingprovider.h>
-#include <script/standard.h>
#include <test/fuzz/fuzz.h>
#include <cassert>
@@ -39,12 +36,4 @@ FUZZ_TARGET_INIT(key_io, initialize_key_io)
if (ext_pub_key.pubkey.size() == CPubKey::COMPRESSED_SIZE) {
assert(ext_pub_key == DecodeExtPubKey(EncodeExtPubKey(ext_pub_key)));
}
-
- const CTxDestination tx_destination = DecodeDestination(random_string);
- (void)DescribeAddress(tx_destination);
- (void)GetKeyForDestination(/* store */ {}, tx_destination);
- (void)GetScriptForDestination(tx_destination);
- (void)IsValidDestination(tx_destination);
-
- (void)IsValidDestinationString(random_string);
}
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index dbd0c76d42..bfa977520b 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -13,9 +13,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_load_external_block_file()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
@@ -27,5 +32,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
return;
}
FlatFilePos flat_file_pos;
- ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+ g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
}
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index f9d8129ca9..6cb81901cb 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -54,7 +54,7 @@ FUZZ_TARGET(netaddress)
(void)net_addr.IsRFC3927();
(void)net_addr.IsRFC3964();
if (net_addr.IsRFC4193()) {
- assert(net_addr.GetNetwork() == Network::NET_ONION || net_addr.GetNetwork() == Network::NET_INTERNAL || net_addr.GetNetwork() == Network::NET_UNROUTABLE);
+ assert(net_addr.GetNetwork() == Network::NET_INTERNAL || net_addr.GetNetwork() == Network::NET_UNROUTABLE);
}
(void)net_addr.IsRFC4380();
(void)net_addr.IsRFC4843();
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index 70ffc6bf37..a3f71426fa 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -31,7 +31,7 @@ FUZZ_TARGET(node_eviction)
/* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
/* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
/* m_is_local */ fuzzed_data_provider.ConsumeBool(),
- /* m_is_onion */ fuzzed_data_provider.ConsumeBool(),
+ /* m_network */ fuzzed_data_provider.PickValueInArray(ALL_NETWORKS),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 7b99193ad0..c4e4d4c785 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -58,7 +58,19 @@ void initialize_process_message()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
+
+ // Temporary debug for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=35027
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index b87bcf2ef5..950ee45d1d 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -6,8 +6,10 @@
#include <compressor.h>
#include <core_io.h>
#include <core_memusage.h>
+#include <key_io.h>
#include <policy/policy.h>
#include <pubkey.h>
+#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/interpreter.h>
#include <script/script.h>
@@ -184,26 +186,26 @@ FUZZ_TARGET_INIT(script, initialize_script)
}
{
- WitnessUnknown witness_unknown_1{};
- witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
- const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
- witness_unknown_1.length = witness_unknown_program_1.size();
- std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown_1.program);
-
- WitnessUnknown witness_unknown_2{};
- witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
- const std::vector<uint8_t> witness_unknown_program_2 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
- witness_unknown_2.length = witness_unknown_program_2.size();
- std::copy(witness_unknown_program_2.begin(), witness_unknown_program_2.end(), witness_unknown_2.program);
-
- (void)(witness_unknown_1 == witness_unknown_2);
- (void)(witness_unknown_1 < witness_unknown_2);
- }
+ const CTxDestination tx_destination_1{
+ fuzzed_data_provider.ConsumeBool() ?
+ DecodeDestination(fuzzed_data_provider.ConsumeRandomLengthString()) :
+ ConsumeTxDestination(fuzzed_data_provider)};
+ const CTxDestination tx_destination_2{ConsumeTxDestination(fuzzed_data_provider)};
+ const std::string encoded_dest{EncodeDestination(tx_destination_1)};
+ const UniValue json_dest{DescribeAddress(tx_destination_1)};
+ Assert(tx_destination_1 == DecodeDestination(encoded_dest));
+ (void)GetKeyForDestination(/* store */ {}, tx_destination_1);
+ const CScript dest{GetScriptForDestination(tx_destination_1)};
+ const bool valid{IsValidDestination(tx_destination_1)};
+ Assert(dest.empty() != valid);
+
+ Assert(valid == IsValidDestinationString(encoded_dest));
- {
- const CTxDestination tx_destination_1 = ConsumeTxDestination(fuzzed_data_provider);
- const CTxDestination tx_destination_2 = ConsumeTxDestination(fuzzed_data_provider);
- (void)(tx_destination_1 == tx_destination_2);
(void)(tx_destination_1 < tx_destination_2);
+ if (tx_destination_1 == tx_destination_2) {
+ Assert(encoded_dest == EncodeDestination(tx_destination_2));
+ Assert(json_dest.write() == DescribeAddress(tx_destination_2).write());
+ Assert(dest == GetScriptForDestination(tx_destination_2));
+ }
}
}
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index bcf0b0ce72..ece3214ed5 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <pubkey.h>
#include <test/fuzz/util.h>
#include <test/util/script.h>
#include <util/rbf.h>
@@ -304,3 +305,41 @@ uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
}) :
fuzzed_data_provider.ConsumeIntegral<uint32_t>();
}
+
+CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ CTxDestination tx_destination;
+ const size_t call_size{CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ tx_destination = CNoDestination{};
+ },
+ [&] {
+ tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
+ },
+ [&] {
+ tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
+ },
+ [&] {
+ tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
+ },
+ [&] {
+ tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
+ },
+ [&] {
+ tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
+ },
+ [&] {
+ WitnessUnknown witness_unknown{};
+ witness_unknown.version = fuzzed_data_provider.ConsumeIntegralInRange(2, 16);
+ std::vector<uint8_t> witness_unknown_program_1{fuzzed_data_provider.ConsumeBytes<uint8_t>(40)};
+ if (witness_unknown_program_1.size() < 2) {
+ witness_unknown_program_1 = {0, 0};
+ }
+ witness_unknown.length = witness_unknown_program_1.size();
+ std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
+ tx_destination = witness_unknown;
+ })};
+ Assert(call_size == std::variant_size_v<CTxDestination>);
+ return tx_destination;
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 023dcdb3e5..9f09395a9a 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -37,7 +37,7 @@
#include <vector>
template <typename... Callables>
-void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
+size_t CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
{
constexpr size_t call_size{sizeof...(callables)};
static_assert(call_size >= 1);
@@ -45,6 +45,7 @@ void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
size_t i{0};
((i++ == call_index ? callables() : void()), ...);
+ return call_size;
}
template <typename Collection>
@@ -178,36 +179,7 @@ template <typename WeakEnumType, size_t size>
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
}
-[[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- CTxDestination tx_destination;
- CallOneOf(
- fuzzed_data_provider,
- [&] {
- tx_destination = CNoDestination{};
- },
- [&] {
- tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
- },
- [&] {
- tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
- },
- [&] {
- tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
- },
- [&] {
- tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
- },
- [&] {
- WitnessUnknown witness_unknown{};
- witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
- const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
- witness_unknown.length = witness_unknown_program_1.size();
- std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
- tx_destination = witness_unknown;
- });
- return tx_destination;
-}
+[[nodiscard]] CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept;
template <typename T>
[[nodiscard]] bool MultiplicationOverflow(const T i, const T j) noexcept
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index e1a21b6c53..c2aaf486c5 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -14,9 +14,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_validation_load_mempool()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
@@ -29,6 +34,6 @@ FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
auto fuzzed_fopen = [&](const fs::path&, const char*) {
return fuzzed_file_provider.open();
};
- (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
+ (void)LoadMempool(pool, g_setup->m_node.chainman->ActiveChainstate(), fuzzed_fopen);
(void)DumpMempool(pool, fuzzed_fopen, true);
}
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index 73463b071e..44779f7d7c 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
auto* orig_tip = active.Tip();
for (int i = 0; i < 10; ++i) {
BlockValidationState state;
- ChainstateActive().InvalidateBlock(state, Params(), active.Tip());
+ m_node.chainman->ActiveChainstate().InvalidateBlock(state, active.Tip());
}
BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10);
coinbaseKey.MakeNewKey(true);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index cb66d5164e..b915982d98 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -300,6 +300,48 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
auto sig = ParseHex(test.first[2]);
BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
}
+
+ static const std::vector<std::array<std::string, 5>> SIGN_VECTORS = {
+ {{"0000000000000000000000000000000000000000000000000000000000000003", "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}},
+ {{"B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "0000000000000000000000000000000000000000000000000000000000000001", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}},
+ {{"C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}},
+ {{"0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}},
+ };
+
+ for (const auto& [sec_hex, pub_hex, aux_hex, msg_hex, sig_hex] : SIGN_VECTORS) {
+ auto sec = ParseHex(sec_hex);
+ auto pub = ParseHex(pub_hex);
+ uint256 aux256(ParseHex(aux_hex));
+ uint256 msg256(ParseHex(msg_hex));
+ auto sig = ParseHex(sig_hex);
+ unsigned char sig64[64];
+
+ // Run the untweaked test vectors above, comparing with exact expected signature.
+ CKey key;
+ key.Set(sec.begin(), sec.end(), true);
+ XOnlyPubKey pubkey(key.GetPubKey());
+ BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end()));
+ bool ok = key.SignSchnorr(msg256, sig64, nullptr, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
+ // Verify those signatures for good measure.
+ BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));
+
+ // Do 10 iterations where we sign with a random Merkle root to tweak,
+ // and compare against the resulting tweaked keys, with random aux.
+ // In iteration i=0 we tweak with empty Merkle tree.
+ for (int i = 0; i < 10; ++i) {
+ uint256 merkle_root;
+ if (i) merkle_root = InsecureRand256();
+ auto tweaked = pubkey.CreateTapTweak(i ? &merkle_root : nullptr);
+ BOOST_CHECK(tweaked);
+ XOnlyPubKey tweaked_key = tweaked->first;
+ aux256 = InsecureRand256();
+ bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
+ }
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index e54948a840..e20c5e4e8f 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -45,7 +45,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(::ChainstateActive(), *m_node.mempool, params, options);
+ return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
}
constexpr static struct {
@@ -82,11 +82,11 @@ constexpr static struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = ::ChainActive().Tip();
+ index.pprev = active_chain_tip;
return index;
}
@@ -228,17 +228,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
+ pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
- txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
+ txCoinbase.vin[0].scriptSig.push_back(m_node.chainman->ActiveChain().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = ::ChainActive().Height();
+ baseheight = m_node.chainman->ActiveChain().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -364,29 +364,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// subsidy changing
- int nHeight = ::ChainActive().Height();
+ int nHeight = m_node.chainman->ActiveChain().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (::ChainActive().Tip()->nHeight < 209999) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (::ChainActive().Tip()->nHeight < 210000) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -409,16 +409,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// Delete the dummy blocks again.
- while (::ChainActive().Tip()->nHeight > nHeight) {
- CBlockIndex* del = ::ChainActive().Tip();
- ::ChainActive().SetTip(del->pprev);
- ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
+ while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
+ m_node.chainman->ActiveChain().SetTip(del->pprev);
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = m_node.chainman->ActiveChain().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -438,53 +438,62 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
+ }
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
+ }
+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
+ prevheights[0] = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -502,14 +511,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- ::ChainActive().Tip()->nHeight++;
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ m_node.chainman->ActiveChain().Tip()->nHeight++;
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- ::ChainActive().Tip()->nHeight--;
+ m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
m_node.mempool->clear();
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 31d391bf7d..5eb280b498 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -2,7 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
#include <net.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -15,33 +17,6 @@
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
-namespace {
-constexpr int NODE_EVICTION_TEST_ROUNDS{10};
-constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
-} // namespace
-
-std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
-{
- std::vector<NodeEvictionCandidate> candidates;
- for (int id = 0; id < n_candidates; ++id) {
- candidates.push_back({
- /* id */ id,
- /* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
- /* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
- /* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
- /* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
- /* fRelevantServices */ random_context.randbool(),
- /* fRelayTxes */ random_context.randbool(),
- /* fBloomFilter */ random_context.randbool(),
- /* nKeyedNetGroup */ random_context.randrange(100),
- /* prefer_evict */ random_context.randbool(),
- /* m_is_local */ random_context.randbool(),
- /* m_is_onion */ random_context.randbool(),
- });
- }
- return candidates;
-}
-
// Create `num_peers` random nodes, apply setup function `candidate_setup_fn`,
// call ProtectEvictionCandidatesByRatio() to apply protection logic, and then
// return true if all of `protected_peer_ids` and none of `unprotected_peer_ids`
@@ -94,7 +69,8 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
@@ -104,129 +80,359 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [num_peers](NodeEvictionCandidate& c) {
c.nTimeConnected = num_peers - c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
random_context));
- // Test protection of onion and localhost peers...
+ // Test protection of onion, localhost, and I2P peers...
// Expect 1/4 onion peers to be protected from eviction,
- // independently of other characteristics.
+ // if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = (c.id == 3 || c.id == 8 || c.id == 9);
+ c.m_is_local = false;
+ c.m_network = (c.id == 3 || c.id == 8 || c.id == 9) ? NET_ONION : NET_IPV4;
},
/* protected_peer_ids */ {3, 8, 9},
/* unprotected_peer_ids */ {},
random_context));
- // Expect 1/4 onion peers and 1/4 of the others to be protected
- // from eviction, sorted by longest uptime (lowest nTimeConnected).
+ // Expect 1/4 onion peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
c.m_is_local = false;
- c.m_is_onion = (c.id == 3 || c.id > 7);
+ c.m_network = (c.id == 3 || c.id > 7) ? NET_ONION : NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 3, 8, 9},
/* unprotected_peer_ids */ {4, 5, 6, 7, 10, 11},
random_context));
// Expect 1/4 localhost peers to be protected from eviction,
- // if no onion peers.
+ // if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = false;
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {1, 9, 11},
/* unprotected_peer_ids */ {},
random_context));
// Expect 1/4 localhost peers and 1/4 of the other peers to be protected,
- // sorted by longest uptime (lowest nTimeConnected), if no onion peers.
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = false;
c.m_is_local = (c.id > 6);
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
random_context));
- // Combined test: expect 1/4 onion and 2 localhost peers to be protected
- // from eviction, sorted by longest uptime.
+ // Expect 1/4 I2P peers to be protected from eviction,
+ // if no onion or localhost peers.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
+ c.m_is_local = false;
+ c.m_network = (c.id == 2 || c.id == 7 || c.id == 10) ? NET_I2P : NET_IPV4;
+ },
+ /* protected_peer_ids */ {2, 7, 10},
+ /* unprotected_peer_ids */ {},
+ random_context));
+
+ // Expect 1/4 I2P peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or localhost peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 0 || c.id == 5 || c.id == 10);
- c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_is_local = false;
+ c.m_network = (c.id == 4 || c.id > 8) ? NET_I2P : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 2, 5, 9, 10},
- /* unprotected_peer_ids */ {3, 4, 6, 7, 8, 11},
+ /* protected_peer_ids */ {0, 1, 2, 4, 9, 10},
+ /* unprotected_peer_ids */ {3, 5, 6, 7, 8, 11},
random_context));
- // Combined test: expect having only 1 onion to allow allocating the
- // remaining 2 of the 1/4 to localhost peers, sorted by longest uptime.
+ // Tests with 2 networks...
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 4 to
+ // protect 1 localhost, 0 onion and 1 other peer, sorted by longest uptime;
+ // stable sort breaks tie with array order of localhost first.
BOOST_CHECK(IsProtected(
- num_peers + 4, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 15);
- c.m_is_local = (c.id > 6 && c.id < 11);
+ c.m_is_local = (c.id == 4);
+ c.m_network = (c.id == 3) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
- /* unprotected_peer_ids */ {4, 5, 6, 10, 11, 12, 13, 14},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 7 to
+ // protect 1 localhost, 0 onion, and 2 other peers (3 total), sorted by
+ // uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 8 to
+ // protect protect 1 localhost, 1 onion and 2 other peers (4 total), sorted
+ // by uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
random_context));
- // Combined test: expect 2 onions (< 1/4) to allow allocating the minimum 2
- // localhost peers, sorted by longest uptime.
+ // Combined test: expect having 3 localhost and 3 onion peers out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest
+ // uptime; stable sort breaks ties with the array order of localhost first.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 7 || c.id == 9);
- c.m_is_local = (c.id == 6 || c.id == 11);
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11);
+ c.m_network = (c.id == 7 || c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 6, 7, 9, 11},
- /* unprotected_peer_ids */ {2, 3, 4, 5, 8, 10},
+ /* protected_peer_ids */ {0, 1, 2, 6, 7, 9},
+ /* unprotected_peer_ids */ {3, 4, 5, 8, 10, 11},
random_context));
- // Combined test: when > 1/4, expect max 1/4 onion and 2 localhost peers
- // to be protected from eviction, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 1 onion peer out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 3 && c.id < 8);
- c.m_is_local = (c.id > 7);
+ c.m_is_local = (c.id > 4 && c.id < 9);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 4, 5, 6, 8, 9},
- /* unprotected_peer_ids */ {1, 2, 3, 7, 10, 11},
+ /* protected_peer_ids */ {0, 1, 2, 5, 6, 10},
+ /* unprotected_peer_ids */ {3, 4, 7, 8, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 8 peers: expect 2 onion and 2
- // localhost peers (1/4 + 2) to be protected, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 2 onion peers out of 16 to
+ // protect 2 localhost and 2 onions, plus 4 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
- 8, [](NodeEvictionCandidate& c) {
+ 16, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 1 && c.id < 5);
- c.m_is_local = (c.id > 4);
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12);
+ c.m_network = (c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 8, 9, 10},
+ /* unprotected_peer_ids */ {4, 5, 7, 11, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 5 localhost and 1 onion peer out of 16 to
+ // protect 3 localhost (recovering the unused onion slot), 1 onion, and 4
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 10);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 10, 11, 12, 13},
+ /* unprotected_peer_ids */ {4, 5, 6, 7, 8, 9, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 4 onion peers out of 16 to
+ // protect 1 localhost and 3 onions (recovering the unused localhost slot),
+ // plus 4 others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ c.m_network = (c.id > 6 && c.id < 11) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
+ /* unprotected_peer_ids */ {5, 6, 10, 11, 12, 13, 14},
+ random_context));
+
+ // Combined test: expect having 2 onion and 4 I2P out of 12 peers to protect
+ // 2 onion (prioritized for having fewer candidates) and 1 I2P, plus 3
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = false;
+ if (c.id == 8 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else if (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12) {
+ c.m_network = NET_I2P;
+ } else {
+ c.m_network = NET_IPV4;
+ }
},
- /* protected_peer_ids */ {2, 3, 5, 6},
- /* unprotected_peer_ids */ {0, 1, 4, 7},
+ /* protected_peer_ids */ {0, 1, 2, 6, 8, 10},
+ /* unprotected_peer_ids */ {3, 4, 5, 7, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 6 peers: expect 1 onion peer and no
- // localhost peers (1/4 + 0) to be protected, sorted by longest uptime.
+ // Tests with 3 networks...
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 4
+ // to protect 1 I2P, 0 localhost, 0 onion and 1 other peer (2 total), sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
BOOST_CHECK(IsProtected(
- 6, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 4 || c.id == 5);
c.m_is_local = (c.id == 3);
+ if (c.id == 4) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 2) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 7
+ // to protect 1 I2P, 0 localhost, 0 onion and 2 other peers (3 total) sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 4);
+ if (c.id == 6) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 5) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 8
+ // to protect 1 I2P, 1 localhost, 0 onion and 2 other peers (4 total) sorted
+ // by uptime; stable sort breaks tie with array order of I2P then localhost.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ if (c.id == 5) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 4) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
+ random_context));
+
+ // Combined test: expect having 4 localhost, 2 I2P, and 2 onion peers out of
+ // 16 to protect 1 localhost, 2 I2P, and 1 onion (4/16 total), plus 4 others
+ // for 8 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id > 11);
+ if (c.id == 7 || c.id == 11) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 9 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 7, 9, 11},
+ /* unprotected_peer_ids */ {4, 5, 8, 10, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 8 I2P and 1 onion peer out of
+ // 24 to protect 1, 4, and 1 (6 total), plus 6 others for 12/24 total,
+ // sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 12);
+ if (c.id > 14 && c.id < 23) { // 4 protected instead of usual 2
+ c.m_network = NET_I2P;
+ } else if (c.id == 23) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
},
- /* protected_peer_ids */ {0, 1, 4},
- /* unprotected_peer_ids */ {2, 3, 5},
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 15, 16, 17, 18, 23},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 14, 19, 20, 21, 22},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 3 I2P and 6 onion peers out of
+ // 24 to protect 1, 3, and 2 (6 total, I2P has fewer candidates and so gets the
+ // unused localhost slot), plus 6 others for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ if (c.id == 12 || c.id == 14 || c.id == 17) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 17) { // 4 protected instead of usual 2
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 14, 15, 17, 18, 19},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 16, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 7 I2P and 4 onion peers out of
+ // 24 to protect 1 localhost, 2 I2P, and 3 onions (6 total), plus 6 others
+ // for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 13);
+ if (c.id > 16) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 12 || c.id == 14 || c.id == 15 || c.id == 16) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 17, 18},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 16, 19, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 8 localhost, 4 I2P, and 3 onion peers out of
+ // 24 to protect 2 of each (6 total), plus 6 others for 12/24 total, sorted
+ // by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 15);
+ if (c.id > 10 && c.id < 15) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 6 && c.id < 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 16, 17},
+ /* unprotected_peer_ids */ {6, 9, 10, 13, 14, 15, 18, 19, 20, 21, 22, 23},
random_context));
}
@@ -257,91 +463,89 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
{
FastRandomContext random_context{true};
- for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
- for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
- // Four nodes with the highest keyed netgroup values should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Eight nodes with the lowest minimum ping time should be protected
- // from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four nodes that most recently sent us novel transactions accepted
- // into our mempool should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastTXTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Up to eight non-tx-relay peers that most recently sent us novel
- // blocks should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four peers that most recently sent us novel blocks should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Combination of the previous two tests.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
-
- // Combination of all tests above.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
-
- // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
- // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
- // peers by last novel block time, and four more peers by last novel block time.
- if (number_of_nodes >= 29) {
- BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
-
- // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
- // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
- // novel block time.
- if (number_of_nodes <= 20) {
- BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
+ for (int number_of_nodes = 0; number_of_nodes < 200; ++number_of_nodes) {
+ // Four nodes with the highest keyed netgroup values should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Eight nodes with the lowest minimum ping time should be protected
+ // from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [](NodeEvictionCandidate& candidate) {
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four nodes that most recently sent us novel transactions accepted
+ // into our mempool should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastTXTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Up to eight non-tx-relay peers that most recently sent us novel
+ // blocks should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four peers that most recently sent us novel blocks should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Combination of the previous two tests.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
+
+ // Combination of all tests above.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
+
+ // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
+ // peers by last novel block time, and four more peers by last novel block time.
+ if (number_of_nodes >= 29) {
+ BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
+ }
- // Cases left to test:
- // * "If any remaining peers are preferred for eviction consider only them. [...]"
- // * "Identify the network group with the most connections and youngest member. [...]"
+ // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
+ // novel block time.
+ if (number_of_nodes <= 20) {
+ BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
}
+
+ // Cases left to test:
+ // * "If any remaining peers are preferred for eviction consider only them. [...]"
+ // * "Identify the network group with the most connections and youngest member. [...]"
}
}
diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp
index 54e07b0f61..7876c0bcda 100644
--- a/src/test/serfloat_tests.cpp
+++ b/src/test/serfloat_tests.cpp
@@ -36,9 +36,9 @@ uint64_t TestDouble(double f) {
} // namespace
BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
- BOOST_CHECK_EQUAL(TestDouble(0.0), 0);
+ BOOST_CHECK_EQUAL(TestDouble(0.0), 0U);
BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000);
- BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000U);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::infinity()), 0xfff0000000000000);
BOOST_CHECK_EQUAL(TestDouble(0.5), 0x3fe0000000000000ULL);
BOOST_CHECK_EQUAL(TestDouble(1.0), 0x3ff0000000000000ULL);
@@ -48,8 +48,8 @@ BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
// Roundtrip test on IEC559-compatible systems
if (std::numeric_limits<double>::is_iec559) {
- BOOST_CHECK_EQUAL(sizeof(double), 8);
- BOOST_CHECK_EQUAL(sizeof(uint64_t), 8);
+ BOOST_CHECK_EQUAL(sizeof(double), 8U);
+ BOOST_CHECK_EQUAL(sizeof(uint64_t), 8U);
// Test extreme values
TestDouble(std::numeric_limits<double>::min());
TestDouble(-std::numeric_limits<double>::min());
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 7af2b79f37..acd0151e1a 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue)
uint32_t varint = 0;
// Deserialize into r-value
reader >> VARINT(varint);
- BOOST_CHECK_EQUAL(varint, 54321);
+ BOOST_CHECK_EQUAL(varint, 54321U);
BOOST_CHECK(reader.empty());
}
diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp
index 940145b84f..e97eab2c00 100644
--- a/src/test/system_tests.cpp
+++ b/src/test/system_tests.cpp
@@ -7,6 +7,11 @@
#include <univalue.h>
#ifdef ENABLE_EXTERNAL_SIGNER
+#if defined(WIN32) && !defined(__kernel_entry)
+// A workaround for boost 1.71 incompatibility with mingw-w64 compiler.
+// For details see https://github.com/bitcoin/bitcoin/pull/22348.
+#define __kernel_entry
+#endif
#include <boost/process.hpp>
#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index d47c54fd6e..3ce7ecb5f2 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(txindex.Start(::ChainstateActive()));
+ BOOST_REQUIRE(txindex.Start(m_node.chainman->ActiveChainstate()));
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 95ad85d0f8..ade9e210f2 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -37,7 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
true /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 3244b58082..23195c0a26 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(tx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
true /* bypass_limits */);
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
@@ -63,7 +63,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
// Test 2: ... and should be rejected if spend1 is in the memory pool
@@ -71,7 +71,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -80,7 +80,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -91,7 +91,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
@@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
// If we add many more flags, this loop can get too expensive, but we can
@@ -126,7 +126,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -136,13 +136,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -205,20 +205,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -227,8 +227,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -244,7 +244,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
// Test CHECKLOCKTIMEVERIFY
@@ -267,13 +267,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -295,13 +295,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -324,11 +324,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
{
@@ -353,7 +353,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -363,12 +363,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 1204873828..f6a11bc02e 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -74,12 +74,12 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{::ChainstateActive(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
LOCK(cs_main);
- block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
+ block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
block->hashMerkleRoot = BlockMerkleRoot(*block);
return block;
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 847a490e03..28d7967078 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -6,6 +6,9 @@
#include <chainparams.h>
#include <net.h>
+#include <span.h>
+
+#include <vector>
void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, Span<const uint8_t> msg_bytes, bool& complete) const
{
@@ -37,3 +40,25 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) con
NodeReceiveMsgBytes(node, ser_msg.data, complete);
return complete;
}
+
+std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context)
+{
+ std::vector<NodeEvictionCandidate> candidates;
+ for (int id = 0; id < n_candidates; ++id) {
+ candidates.push_back({
+ /* id */ id,
+ /* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
+ /* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
+ /* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
+ /* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
+ /* fRelevantServices */ random_context.randbool(),
+ /* fRelayTxes */ random_context.randbool(),
+ /* fBloomFilter */ random_context.randbool(),
+ /* nKeyedNetGroup */ random_context.randrange(100),
+ /* prefer_evict */ random_context.randbool(),
+ /* m_is_local */ random_context.randbool(),
+ /* m_network */ ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
+ });
+ }
+ return candidates;
+}
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 71685d437a..939ec322ed 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -6,9 +6,11 @@
#define BITCOIN_TEST_UTIL_NET_H
#include <compat.h>
+#include <netaddress.h>
#include <net.h>
#include <util/sock.h>
+#include <array>
#include <cassert>
#include <cstring>
#include <string>
@@ -67,6 +69,16 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
ConnectionType::ADDR_FETCH,
};
+constexpr auto ALL_NETWORKS = std::array{
+ Network::NET_UNROUTABLE,
+ Network::NET_IPV4,
+ Network::NET_IPV6,
+ Network::NET_ONION,
+ Network::NET_I2P,
+ Network::NET_CJDNS,
+ Network::NET_INTERNAL,
+};
+
/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
@@ -129,4 +141,6 @@ private:
mutable size_t m_consumed;
};
+std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context);
+
#endif // BITCOIN_TEST_UTIL_NET_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 863c3ab565..d9d236be1d 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -76,6 +76,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{}
{
+ m_node.args = &gArgs;
const std::vector<const char*> arguments = Cat(
{
"dummy",
@@ -94,7 +95,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
gArgs.ForceSetArg("-datadir", m_path_root.string());
gArgs.ClearPathCache();
{
- SetupServerArgs(m_node);
+ SetupServerArgs(*m_node.args);
std::string error;
const bool success{m_node.args->ParseParameters(arguments.size(), arguments.data(), error)};
assert(success);
@@ -145,7 +146,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
- m_node.chainman = &::g_chainman;
+ m_node.chainman = std::make_unique<ChainstateManager>();
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
@@ -167,7 +168,7 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
- m_node.chainman = nullptr;
+ m_node.chainman.reset();
pblocktree.reset();
}
@@ -179,23 +180,23 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
- m_node.chainman->InitializeChainstate(*m_node.mempool);
- ::ChainstateActive().InitCoinsDB(
+ m_node.chainman->InitializeChainstate(m_node.mempool.get());
+ m_node.chainman->ActiveChainstate().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
- assert(!::ChainstateActive().CanFlushToDisk());
- ::ChainstateActive().InitCoinsCache(1 << 23);
- assert(::ChainstateActive().CanFlushToDisk());
- if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ m_node.chainman->ActiveChainstate().InitCoinsCache(1 << 23);
+ assert(m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock()) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams)) {
+ if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
m_node.addrman = std::make_unique<CAddrMan>();
- m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ 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,
m_node.banman.get(), *m_node.scheduler, *m_node.chainman,
@@ -240,7 +241,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(::ChainstateActive(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -299,7 +300,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 552be0a2da..e0bc10d660 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -84,8 +84,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
- LOCK(cs_main); // For g_chainman.m_blockman.LookupBlockIndex
- GenerateCoinbaseCommitment(*pblock, g_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+ LOCK(cs_main); // For m_node.chainman->m_blockman.LookupBlockIndex
+ GenerateCoinbaseCommitment(*pblock, m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = ::ChainActive().Tip();
+ initial_tip = m_node.chainman->ActiveChain().Tip();
}
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterSharedValidationInterface(sub);
@@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterSharedValidationInterface(sub);
LOCK(cs_main);
- BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(sub->m_expected_tip, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
/**
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Run the test multiple times
for (int test_runs = 3; test_runs > 0; --test_runs) {
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// Later on split from here
const uint256 split_hash{last_mined->hashPrevBlock};
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
LOCK(cs_main);
for (const auto& tx : txs) {
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, tx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
@@ -306,7 +306,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
}
LOCK(cs_main);
// We are done with the reorg, so the tip must have changed
- assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
+ assert(tip_init != m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}};
// Submit the reorg in this thread to invalidate and remove the txs from the tx pool
@@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
ProcessBlock(b);
}
// Check that the reorg was eventually successful
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// We can join the other thread, which returns when the reorg was successful
rpc_thread.join();
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 92d8cf2e7d..2893b412fb 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
- CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
+ CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 0b912acb08..0bd378631b 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -31,13 +31,12 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
CTxMemPool& mempool = *m_node.mempool;
std::vector<CChainState*> chainstates;
- const CChainParams& chainparams = Params();
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
+ CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -67,7 +66,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
//
const uint256 snapshot_blockhash = GetRandHash();
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
- mempool, snapshot_blockhash));
+ &mempool, snapshot_blockhash));
chainstates.push_back(&c2);
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
@@ -76,9 +75,9 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
// Unlike c1, which doesn't have any blocks. Gets us different tip, height.
- c2.LoadGenesisBlock(chainparams);
+ c2.LoadGenesisBlock();
BlockValidationState _;
- BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr));
+ BOOST_CHECK(c2.ActivateBestChain(_, nullptr));
BOOST_CHECK(manager.IsSnapshotActive());
BOOST_CHECK(!manager.IsSnapshotValidated());
@@ -130,7 +129,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
+ CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -138,7 +137,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
LOCK(::cs_main);
c1.InitCoinsCache(1 << 23);
- BOOST_REQUIRE(c1.LoadGenesisBlock(Params()));
+ BOOST_REQUIRE(c1.LoadGenesisBlock());
c1.CoinsTip().SetBestBlock(InsecureRand256());
manager.MaybeRebalanceCaches();
}
@@ -148,7 +147,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
+ CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -156,7 +155,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
LOCK(::cs_main);
c2.InitCoinsCache(1 << 23);
- BOOST_REQUIRE(c2.LoadGenesisBlock(Params()));
+ BOOST_REQUIRE(c2.LoadGenesisBlock());
c2.CoinsTip().SetBestBlock(InsecureRand256());
manager.MaybeRebalanceCaches();
}
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index a3b344d2c9..22aafcaa6c 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -20,10 +20,9 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{mempool, blockman};
+ CChainState chainstate{&mempool, blockman};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
- CTxMemPool tx_pool{};
constexpr bool is_64_bit = sizeof(void*) == 8;
@@ -57,7 +56,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Without any coins in the cache, we shouldn't need to flush.
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::OK);
// If the initial memory allocations of cacheCoins don't match these common
@@ -72,7 +71,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
}
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::CRITICAL);
BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
@@ -93,7 +92,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::OK);
}
@@ -101,26 +100,26 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
for (int i{0}; i < 4; ++i) {
add_coin(view);
print_view_mem_usage(view);
- if (chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
+ if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
CoinsCacheSizeState::CRITICAL) {
break;
}
}
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::CRITICAL);
// Passing non-zero max mempool usage should allow us more headroom.
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
CoinsCacheSizeState::OK);
for (int i{0}; i < 3; ++i) {
add_coin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
CoinsCacheSizeState::OK);
}
@@ -136,7 +135,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
BOOST_CHECK(usage_percentage >= 0.9);
BOOST_CHECK(usage_percentage < 1);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 1 << 10),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 1 << 10),
CoinsCacheSizeState::LARGE);
}
@@ -144,7 +143,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
for (int i{0}; i < 1000; ++i) {
add_coin(view);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool),
+ chainstate.GetCoinsCacheSizeState(),
CoinsCacheSizeState::OK);
}
@@ -152,7 +151,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// preallocated memory that doesn't get reclaimed even after flush.
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
CoinsCacheSizeState::CRITICAL);
view.SetBestBlock(InsecureRand256());
@@ -160,7 +159,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
- chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
+ chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
CoinsCacheSizeState::CRITICAL);
}
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 304cd8feb0..690031cdc1 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <chainparams.h>
#include <consensus/params.h>
+#include <deploymentstatus.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <versionbits.h>
@@ -258,8 +259,8 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
/** Check that ComputeBlockVersion will set the appropriate bit correctly */
static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
{
- // This implicitly uses versionbitscache, so clear it every time
- versionbitscache.Clear();
+ // This implicitly uses g_versionbitscache, so clear it every time
+ g_versionbitscache.Clear();
int64_t bit = params.vDeployments[dep].bit;
int64_t nStartTime = params.vDeployments[dep].nStartTime;
@@ -267,7 +268,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
int min_activation_height = params.vDeployments[dep].min_activation_height;
// should not be any signalling for first block
- BOOST_CHECK_EQUAL(ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
// always/never active deployments shouldn't need to be tested further
if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE ||
@@ -287,7 +288,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Check min_activation_height is on a retarget boundary
BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0U);
- const uint32_t bitmask{VersionBitsMask(params, dep)};
+ const uint32_t bitmask{g_versionbitscache.Mask(params, dep)};
BOOST_CHECK_EQUAL(bitmask, uint32_t{1} << bit);
// In the first chain, test that the bit is set by CBV until it has failed.
@@ -306,9 +307,9 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// earlier time, so will transition from DEFINED to STARTED at the
// end of the first period by mining blocks at nTime == 0
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// then we'll keep mining at nStartTime...
} else {
// use a time 1s earlier than start time to check we stay DEFINED
@@ -316,28 +317,28 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Start generating blocks before nStartTime
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
// Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// Next we will advance to the next period and transition to STARTED,
}
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
@@ -346,8 +347,8 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
blocksToMine--;
nTime += 600;
nHeight += 1;
@@ -361,7 +362,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// finish the last period before we start timing out
while (nHeight % params.nMinerConfirmationWindow != 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
nHeight += 1;
}
@@ -369,12 +370,12 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// the bit until the period transition.
for (uint32_t i = 0; i < params.nMinerConfirmationWindow - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
nHeight += 1;
}
// The next block should trigger no longer setting the bit.
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// On a new chain:
@@ -385,30 +386,30 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
if (lastBlock->nHeight + 1 < min_activation_height) {
// check signalling continues while min_activation_height is not reached
lastBlock = secondChain.Mine(min_activation_height - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// then reach min_activation_height, which was already REQUIRE'd to start a new period
lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
}
// Check that we don't signal after activation
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
@@ -425,7 +426,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// not take precedence over STARTED/LOCKED_IN. So all softforks on
// the same bit might overlap, even when non-overlapping start-end
// times are picked.
- const uint32_t dep_mask{VersionBitsMask(chainParams->GetConsensus(), dep)};
+ const uint32_t dep_mask{g_versionbitscache.Mask(chainParams->GetConsensus(), dep)};
BOOST_CHECK(!(chain_all_vbits & dep_mask));
chain_all_vbits |= dep_mask;
check_computeblockversion(chainParams->GetConsensus(), dep);
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 762f71feb1..4b76bee5ab 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -168,9 +168,34 @@ bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
return Read(DB_LAST_BLOCK, nFile);
}
-CCoinsViewCursor *CCoinsViewDB::Cursor() const
+/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
+class CCoinsViewDBCursor: public CCoinsViewCursor
{
- CCoinsViewDBCursor *i = new CCoinsViewDBCursor(const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
+public:
+ // Prefer using CCoinsViewDB::Cursor() since we want to perform some
+ // cache warmup on instantiation.
+ CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
+ CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
+ ~CCoinsViewDBCursor() {}
+
+ bool GetKey(COutPoint &key) const override;
+ bool GetValue(Coin &coin) const override;
+ unsigned int GetValueSize() const override;
+
+ bool Valid() const override;
+ void Next() override;
+
+private:
+ std::unique_ptr<CDBIterator> pcursor;
+ std::pair<char, COutPoint> keyTmp;
+
+ friend class CCoinsViewDB;
+};
+
+std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
+{
+ auto i = std::make_unique<CCoinsViewDBCursor>(
+ const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
/* It seems that there are no "const iterators" for LevelDB. Since we
only need read operations on it, use a const-cast to get around
that restriction. */
diff --git a/src/txdb.h b/src/txdb.h
index 0cf7e2f1b8..845d80788f 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -60,7 +60,7 @@ public:
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
- CCoinsViewCursor *Cursor() const override;
+ std::unique_ptr<CCoinsViewCursor> Cursor() const override;
//! Attempt to update from an older database format. Returns whether an error occurred.
bool Upgrade();
@@ -70,28 +70,6 @@ public:
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
-/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
-class CCoinsViewDBCursor: public CCoinsViewCursor
-{
-public:
- ~CCoinsViewDBCursor() {}
-
- bool GetKey(COutPoint &key) const override;
- bool GetValue(Coin &coin) const override;
- unsigned int GetValueSize() const override;
-
- bool Valid() const override;
- void Next() override;
-
-private:
- CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256 &hashBlockIn):
- CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
- std::unique_ptr<CDBIterator> pcursor;
- std::pair<char, COutPoint> keyTmp;
-
- friend class CCoinsViewDB;
-};
-
/** Access to the block database (blocks/index/) */
class CBlockTreeDB : public CDBWrapper
{
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 7c73a2d78d..c5a4bbf1b0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -513,7 +513,6 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
@@ -638,10 +637,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t innerUsage = 0;
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 13ccf7463e..258ba2f235 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -6,6 +6,11 @@
#include <util/system.h>
#ifdef ENABLE_EXTERNAL_SIGNER
+#if defined(WIN32) && !defined(__kernel_entry)
+// A workaround for boost 1.71 incompatibility with mingw-w64 compiler.
+// For details see https://github.com/bitcoin/bitcoin/pull/22348.
+#define __kernel_entry
+#endif
#include <boost/process.hpp>
#endif // ENABLE_EXTERNAL_SIGNER
@@ -620,14 +625,14 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
m_settings.forced_settings[SettingName(strArg)] = strValue;
}
-void ArgsManager::AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat)
+void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
{
Assert(cmd.find('=') == std::string::npos);
Assert(cmd.at(0) != '-');
LOCK(cs_args);
m_accept_any_command = false; // latch to false
- std::map<std::string, Arg>& arg_map = m_available_args[cat];
+ std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
Assert(ret.second); // Fail on duplicate commands
}
@@ -1243,9 +1248,9 @@ void runCommand(const std::string& strCommand)
}
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
{
+#ifdef ENABLE_EXTERNAL_SIGNER
namespace bp = boost::process;
UniValue result_json;
@@ -1277,8 +1282,10 @@ UniValue RunCommandParseJSON(const std::string& str_command, const std::string&
if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
return result_json;
-}
+#else
+ throw std::runtime_error("Compiled without external signing support (required for external signing).");
#endif // ENABLE_EXTERNAL_SIGNER
+}
void SetupEnvironment()
{
diff --git a/src/util/system.h b/src/util/system.h
index c4317c62d0..3547bad585 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -102,7 +102,6 @@ std::string ShellEscape(const std::string& arg);
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
/**
* Execute a command which returns JSON, and parse the result.
*
@@ -111,7 +110,6 @@ void runCommand(const std::string& strCommand);
* @return parsed JSON
*/
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
-#endif // ENABLE_EXTERNAL_SIGNER
/**
* Most paths passed as configuration arguments are treated as relative to
@@ -376,7 +374,7 @@ public:
/**
* Add subcommand
*/
- void AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat);
+ void AddCommand(const std::string& cmd, const std::string& help);
/**
* Add many hidden arguments
diff --git a/src/validation.cpp b/src/validation.cpp
index d5c4be6811..26333d7026 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -15,6 +15,7 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <cuckoocache.h>
+#include <deploymentstatus.h>
#include <flatfile.h>
#include <hash.h>
#include <index/blockfilterindex.h>
@@ -101,21 +102,6 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-ChainstateManager g_chainman;
-
-CChainState& ChainstateActive()
-{
- LOCK(::cs_main);
- assert(g_chainman.m_active_chainstate);
- return *g_chainman.m_active_chainstate;
-}
-
-CChain& ChainActive()
-{
- LOCK(::cs_main);
- return ::ChainstateActive().m_chain;
-}
-
/**
* Mutex to guard access to validation specific variables, such as reading
* or changing the chainstate.
@@ -161,7 +147,6 @@ void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index));
BlockMap::const_iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : it->second;
}
@@ -170,7 +155,6 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
// Find the latest block common to locator and chain - we expect that
// locator.vHave is sorted descending by height.
for (const uint256& hash : locator.vHave) {
@@ -198,7 +182,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
{
AssertLockHeld(cs_main);
assert(active_chain_tip); // TODO: Make active_chain_tip a reference
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
@@ -237,7 +220,6 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
@@ -331,7 +313,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
for (const COutPoint& removed : vNoSpendsRemaining)
coins_cache.Uncache(removed);
}
@@ -339,7 +320,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (active_chainstate.IsInitialBlockDownload())
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
@@ -349,24 +329,14 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
return true;
}
-/* Make mempool consistent after a reorg, by re-adding or recursively erasing
- * disconnected block transactions from the mempool, and also removing any
- * other transactions from the mempool that are no longer valid given the new
- * tip/height.
- *
- * Note: we assume that disconnectpool only contains transactions that are NOT
- * confirmed in the current chain nor already in the mempool (otherwise,
- * in-mempool descendants of such transactions would be removed).
- *
- * Passing fAddToMempool=false will skip trying to add the transactions back,
- * and instead just erase from the mempool as needed.
- */
-
-static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
+void CChainState::MaybeUpdateMempoolForReorg(
+ DisconnectedBlockTransactions& disconnectpool,
+ bool fAddToMempool)
{
+ if (!m_mempool) return;
+
AssertLockHeld(cs_main);
- AssertLockHeld(mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ AssertLockHeld(m_mempool->cs);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -378,11 +348,13 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
if (!fAddToMempool || (*it)->IsCoinBase() ||
- AcceptToMemoryPool(active_chainstate, mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ AcceptToMemoryPool(
+ *this, *m_mempool, *it, true /* bypass_limits */).m_result_type !=
+ MempoolAcceptResult::ResultType::VALID) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
- mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
- } else if (mempool.exists((*it)->GetHash())) {
+ m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
+ } else if (m_mempool->exists((*it)->GetHash())) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
@@ -393,12 +365,16 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
- mempool.UpdateTransactionsFromBlock(vHashUpdate);
+ m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ m_mempool->removeForReorg(*this, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
- LimitMempoolSize(mempool, active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(
+ *m_mempool,
+ this->CoinsTip(),
+ gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
+ std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
}
/**
@@ -433,7 +409,6 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
const Coin& coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout);
assert(!coinFromUTXOSet.IsSpent());
assert(coinFromUTXOSet.out == coin.out);
@@ -454,7 +429,6 @@ public:
m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -607,13 +581,16 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
- // is it already in the memory pool?
- if (m_pool.exists(hash)) {
+ if (m_pool.exists(GenTxid(true, tx.GetWitnessHash()))) {
+ // Exact transaction already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool");
+ } else if (m_pool.exists(GenTxid(false, tx.GetHash()))) {
+ // Transaction with the same non-witness data but different witness (same txid, different
+ // wtxid) already exists in the mempool.
+ return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
}
// Check for conflicts with in-memory transactions
@@ -663,7 +640,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
LockPoints lp;
m_view.SetBackend(m_viewmempool);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
@@ -701,20 +677,16 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// be mined yet.
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
// backend was removed, it no longer pulls coins from the mempool.
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
- const auto& params = args.m_chainparams.GetConsensus();
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
- auto taproot_state = VersionBitsState(m_active_chainstate.m_chain.Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
- if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
+ const bool taproot_active = DeploymentActiveAfter(m_active_chainstate.m_chain.Tip(), args.m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_TAPROOT);
+ if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_active)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}
@@ -739,7 +711,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -992,9 +963,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, P
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
@@ -1035,7 +1004,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
// - the node is not behind
// - the transaction is not dependent on any other transactions in the mempool
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
@@ -1043,7 +1011,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
@@ -1146,7 +1113,6 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
test_accept, /* m_allow_bip125_replacement */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling
@@ -1159,14 +1125,13 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
BlockValidationState state_dummy;
- active_chainstate.FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
+ active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
return result;
}
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
@@ -1182,7 +1147,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
const CChainParams& chainparams = Params();
MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
test_accept, /* m_allow_bip125_replacement */ false };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
// Uncache coins pertaining to transactions that were not submitted to the mempool.
@@ -1245,8 +1209,9 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
+CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
+ m_params(::Params()),
m_blockman(blockman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
@@ -1321,7 +1286,6 @@ static void AlertNotify(const std::string& strMessage)
void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
@@ -1340,7 +1304,6 @@ void CChainState::CheckForkWarningConditions()
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
@@ -1401,7 +1364,6 @@ bool CScriptCheck::operator()() {
int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs)
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
return pindexPrev->nHeight + 1;
}
@@ -1645,23 +1607,6 @@ void StopScriptCheckWorkerThreads()
scriptcheckqueue.StopWorkerThreads();
}
-VersionBitsCache versionbitscache GUARDED_BY(cs_main);
-
-int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
-{
- LOCK(cs_main);
- int32_t nVersion = VERSIONBITS_TOP_BITS;
-
- for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
- ThresholdState state = VersionBitsState(pindexPrev, params, static_cast<Consensus::DeploymentPos>(i), versionbitscache);
- if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) {
- nVersion |= VersionBitsMask(params, static_cast<Consensus::DeploymentPos>(i));
- }
- }
-
- return nVersion;
-}
-
/**
* Threshold condition checker that triggers when unknown versionbits are seen on the network.
*/
@@ -1683,24 +1628,14 @@ public:
return pindex->nHeight >= params.MinBIP9WarningHeight &&
((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
((pindex->nVersion >> bit) & 1) != 0 &&
- ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
+ ((g_versionbitscache.ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
}
};
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
-// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
-// mainnet. We no longer need to support disabling the segwit deployment
-// except for testing purposes, due to limitations of the functional test
-// environment. See test/functional/p2p-segwit.py.
-static bool IsScriptWitnessEnabled(const Consensus::Params& params)
+static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams)
{
- return params.SegwitHeight != std::numeric_limits<int>::max();
-}
-
-static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- AssertLockHeld(cs_main);
-
unsigned int flags = SCRIPT_VERIFY_NONE;
// BIP16 didn't become active until Apr 1 2012 (on mainnet, and
@@ -1717,32 +1652,32 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
// Enforce WITNESS rules whenever P2SH is in effect (and the segwit
// deployment is defined).
- if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) {
+ if (flags & SCRIPT_VERIFY_P2SH && DeploymentEnabled(consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
flags |= SCRIPT_VERIFY_WITNESS;
}
- // Start enforcing the DERSIG (BIP66) rule
- if (pindex->nHeight >= consensusparams.BIP66Height) {
+ // Enforce the DERSIG (BIP66) rule
+ if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_DERSIG)) {
flags |= SCRIPT_VERIFY_DERSIG;
}
- // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule
- if (pindex->nHeight >= consensusparams.BIP65Height) {
+ // Enforce CHECKLOCKTIMEVERIFY (BIP65)
+ if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_CLTV)) {
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
- // Start enforcing BIP112 (CHECKSEQUENCEVERIFY)
- if (pindex->nHeight >= consensusparams.CSVHeight) {
+ // Enforce CHECKSEQUENCEVERIFY (BIP112)
+ if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_CSV)) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
- // Start enforcing Taproot using versionbits logic.
- if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE) {
+ // Enforce Taproot (BIP340-BIP342)
+ if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_TAPROOT)) {
flags |= SCRIPT_VERIFY_TAPROOT;
}
- // Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit)
- if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
+ // Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
+ if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -1764,7 +1699,7 @@ static int64_t nBlocksTotal = 0;
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
* can fail if those validity checks fail (among other reasons). */
bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
+ CCoinsViewCache& view, bool fJustCheck)
{
AssertLockHeld(cs_main);
assert(pindex);
@@ -1784,7 +1719,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
// GetAdjustedTime() to go backward).
- if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) {
+ if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
// corrupted, so this should be impossible unless we're having hardware
@@ -1802,7 +1737,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
- if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) {
+ if (block.GetHash() == m_params.GetConsensus().hashGenesisBlock) {
if (!fJustCheck)
view.SetBestBlock(pindex->GetBlockHash());
return true;
@@ -1834,7 +1769,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// artificially set the default assumed verified block further back.
// The test against nMinimumChainWork prevents the skipping when denied access to any chain at
// least as good as the expected chain.
- fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
+ fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
}
}
}
@@ -1914,9 +1849,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// be reset before it reaches block 1,983,702 and starts doing unnecessary
// BIP30 checking again.
assert(pindex->pprev);
- CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height);
+ CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(m_params.GetConsensus().BIP34Height);
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
- fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
+ fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == m_params.GetConsensus().BIP34Hash));
// TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a
// consensus change that ensures coinbases at those heights can not
@@ -1932,14 +1867,14 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
}
}
- // Start enforcing BIP68 (sequence locks)
+ // Enforce BIP68 (sequence locks)
int nLockTimeFlags = 0;
- if (pindex->nHeight >= chainparams.GetConsensus().CSVHeight) {
+ if (DeploymentActiveAt(*pindex, m_params.GetConsensus(), Consensus::DEPLOYMENT_CSV)) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
// Get the script flags for this block
- unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus());
+ unsigned int flags = GetBlockScriptFlags(pindex, m_params.GetConsensus());
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal);
@@ -2029,7 +1964,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
- CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
+ CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, m_params.GetConsensus());
if (block.vtx[0]->GetValueOut() > blockReward) {
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)\n", block.vtx[0]->GetValueOut(), blockReward);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount");
@@ -2045,8 +1980,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
if (fJustCheck)
return true;
- if (!WriteUndoDataForBlock(blockundo, state, pindex, chainparams))
+ if (!WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
return false;
+ }
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
@@ -2066,20 +2002,18 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return true;
}
-CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(const CTxMemPool* tx_pool)
+CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
{
return this->GetCoinsCacheSizeState(
- tx_pool,
m_coinstip_cache_size_bytes,
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
- const CTxMemPool* tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes)
{
- const int64_t nMempoolUsage = tx_pool ? tx_pool->DynamicMemoryUsage() : 0;
+ const int64_t nMempoolUsage = m_mempool ? m_mempool->DynamicMemoryUsage() : 0;
int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace =
max_coins_cache_size_bytes + std::max<int64_t>(max_mempool_size_bytes - nMempoolUsage, 0);
@@ -2099,7 +2033,6 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
}
bool CChainState::FlushStateToDisk(
- const CChainParams& chainparams,
BlockValidationState &state,
FlushStateMode mode,
int nManualPruneHeight)
@@ -2119,7 +2052,7 @@ bool CChainState::FlushStateToDisk(
bool fFlushForPrune = false;
bool fDoFullFlush = false;
- CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
+ CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
// make sure we don't prune above the blockfilterindexes bestblocks
@@ -2136,7 +2069,7 @@ bool CChainState::FlushStateToDisk(
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
+ m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -2236,20 +2169,19 @@ bool CChainState::FlushStateToDisk(
return true;
}
-void CChainState::ForceFlushStateToDisk() {
+void CChainState::ForceFlushStateToDisk()
+{
BlockValidationState state;
- const CChainParams& chainparams = Params();
- if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
+ if (!this->FlushStateToDisk(state, FlushStateMode::ALWAYS)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
-void CChainState::PruneAndFlush() {
+void CChainState::PruneAndFlush()
+{
BlockValidationState state;
fCheckForPruning = true;
- const CChainParams& chainparams = Params();
-
- if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
+ if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
@@ -2271,12 +2203,12 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
res += warn;
}
-/** Check warning conditions and do some notifications on new chain tip set. */
-static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
- EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+void CChainState::UpdateTip(const CBlockIndex* pindexNew)
{
// New best block
- mempool.AddTransactionsUpdated(1);
+ if (m_mempool) {
+ m_mempool->AddTransactionsUpdated(1);
+ }
{
LOCK(g_best_block_mutex);
@@ -2285,14 +2217,13 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
- if (!active_chainstate.IsInitialBlockDownload()) {
+ if (!this->IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
- ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
+ ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
DoWarning(warning);
} else {
@@ -2301,37 +2232,37 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(),
+ GuessVerificationProgress(m_params.TxData(), pindexNew), this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
/** Disconnect m_chain's tip.
* After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
- * should make the mempool consistent again by calling UpdateMempoolForReorg.
+ * should make the mempool consistent again by calling MaybeUpdateMempoolForReorg.
* with cs_main held.
*
* If disconnectpool is nullptr, then no disconnected transactions are added to
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool)
+bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool)
{
AssertLockHeld(cs_main);
- AssertLockHeld(m_mempool.cs);
+ if (m_mempool) AssertLockHeld(m_mempool->cs);
CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
- if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
+ if (!ReadBlockFromDisk(block, pindexDelete, m_params.GetConsensus())) {
return error("DisconnectTip(): Failed to read block");
+ }
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
@@ -2344,10 +2275,11 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
+ if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
return false;
+ }
- if (disconnectpool) {
+ if (disconnectpool && m_mempool) {
// Save transactions to re-add to mempool at end of reorg
for (auto it = block.vtx.rbegin(); it != block.vtx.rend(); ++it) {
disconnectpool->addTransaction(*it);
@@ -2355,14 +2287,14 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
- m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
+ m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
disconnectpool->removeEntry(it);
}
}
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this);
+ UpdateTip(pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2421,10 +2353,10 @@ public:
*
* The block is added to connectTrace if connection succeeds.
*/
-bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
+bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool)
{
AssertLockHeld(cs_main);
- AssertLockHeld(m_mempool.cs);
+ if (m_mempool) AssertLockHeld(m_mempool->cs);
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
@@ -2432,8 +2364,9 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
- if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
+ if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_params.GetConsensus())) {
return AbortNode(state, "Failed to read block");
+ }
pthisBlock = pblockNew;
} else {
pthisBlock = pblock;
@@ -2445,7 +2378,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{
CCoinsViewCache view(&CoinsTip());
- bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
+ bool rv = ConnectBlock(blockConnecting, state, pindexNew, view);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
if (state.IsInvalid())
@@ -2461,16 +2394,19 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
+ if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
return false;
+ }
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
- m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
- disconnectpool.removeForBlock(blockConnecting.vtx);
+ if (m_mempool) {
+ m_mempool->removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
+ disconnectpool.removeForBlock(blockConnecting.vtx);
+ }
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(m_mempool, pindexNew, chainparams, *this);
+ UpdateTip(pindexNew);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2557,11 +2493,10 @@ void CChainState::PruneBlockIndexCandidates() {
*
* @returns true unless a system error occurred
*/
-bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
+bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
- AssertLockHeld(m_mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ if (m_mempool) AssertLockHeld(m_mempool->cs);
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2570,10 +2505,10 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
bool fBlocksDisconnected = false;
DisconnectedBlockTransactions disconnectpool;
while (m_chain.Tip() && m_chain.Tip() != pindexFork) {
- if (!DisconnectTip(state, chainparams, &disconnectpool)) {
+ if (!DisconnectTip(state, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
+ MaybeUpdateMempoolForReorg(disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2603,7 +2538,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// Connect new blocks.
for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) {
- if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
+ if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
@@ -2617,7 +2552,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
+ MaybeUpdateMempoolForReorg(disconnectpool, false);
return false;
}
} else {
@@ -2634,9 +2569,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
+ MaybeUpdateMempoolForReorg(disconnectpool, true);
}
- m_mempool.check(*this);
+ if (m_mempool) m_mempool->check(*this);
CheckForkWarningConditions();
@@ -2661,7 +2596,6 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
fInitialBlockDownload = chainstate.IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
@@ -2681,7 +2615,8 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
-bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
+bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
+{
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
// us in the middle of ProcessNewBlock - do not assume pblock is set
@@ -2708,7 +2643,8 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
{
LOCK(cs_main);
- LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
+ // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
+ LOCK(MempoolMutex());
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@@ -2727,7 +2663,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
+ if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
// A system error occurred
return false;
}
@@ -2769,17 +2705,17 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
// that the best block hash is non-null.
if (ShutdownRequested()) break;
} while (pindexNewTip != pindexMostWork);
- CheckBlockIndex(chainparams.GetConsensus());
+ CheckBlockIndex();
// Write changes periodically to disk, after relay.
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::PERIODIC)) {
+ if (!FlushStateToDisk(state, FlushStateMode::PERIODIC)) {
return false;
}
return true;
}
-bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex)
+bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
{
{
LOCK(cs_main);
@@ -2805,10 +2741,10 @@ bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams&
}
}
- return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
+ return ActivateBestChain(state, std::shared_ptr<const CBlock>());
}
-bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
+bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
{
// Genesis block can't be invalidated
assert(pindex);
@@ -2858,7 +2794,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
LimitValidationInterfaceQueue();
LOCK(cs_main);
- LOCK(m_mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
+ // Lock for as long as disconnectpool is in scope to make sure MaybeUpdateMempoolForReorg is
+ // called after DisconnectTip without unlocking in between
+ LOCK(MempoolMutex());
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -2866,14 +2804,13 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// ActivateBestChain considers blocks already in m_chain
// unconditionally valid already, so force disconnect away from it.
DisconnectedBlockTransactions disconnectpool;
- bool ret = DisconnectTip(state, chainparams, &disconnectpool);
+ bool ret = DisconnectTip(state, &disconnectpool);
// DisconnectTip will add transactions to disconnectpool.
// Adjust the mempool to be consistent with the new tip, adding
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
- UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ MaybeUpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -2909,7 +2846,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
to_mark_failed = invalid_walk_tip;
}
- CheckBlockIndex(chainparams.GetConsensus());
+ CheckBlockIndex();
{
LOCK(cs_main);
@@ -3020,7 +2957,7 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams)
+void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -3028,7 +2965,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
- if (IsWitnessEnabled(pindexNew->pprev, consensusParams)) {
+ if (DeploymentActiveAt(*pindexNew, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
@@ -3149,17 +3086,11 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
return true;
}
-bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
-{
- int height = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
- return (height >= params.SegwitHeight);
-}
-
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
{
int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00);
- if (commitpos != NO_WITNESS_COMMITMENT && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
+ if (commitpos != NO_WITNESS_COMMITMENT && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT) && !block.vtx[0]->HasWitness()) {
CMutableTransaction tx(*block.vtx[0]);
tx.vin[0].scriptWitness.stack.resize(1);
tx.vin[0].scriptWitness.stack[0] = nonce;
@@ -3172,7 +3103,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
- if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
+ if (DeploymentEnabled(consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
if (commitpos == NO_WITNESS_COMMITMENT) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
@@ -3203,7 +3134,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
{
const uint256& hash = i.second;
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
@@ -3236,7 +3166,6 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@@ -3252,13 +3181,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
- // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
- // check for version 2, 3 and 4 upgrades
- if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
- (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
- (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
+ // Reject blocks with outdated version
+ if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB)) ||
+ (block.nVersion < 3 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DERSIG)) ||
+ (block.nVersion < 4 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CLTV))) {
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
+ }
return true;
}
@@ -3273,9 +3202,9 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
{
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
- // Start enforcing BIP113 (Median Time Past).
+ // Enforce BIP113 (Median Time Past).
int nLockTimeFlags = 0;
- if (nHeight >= consensusParams.CSVHeight) {
+ if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV)) {
assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3292,7 +3221,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
}
// Enforce rule that the coinbase starts with serialized block height
- if (nHeight >= consensusParams.BIP34Height)
+ if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
@@ -3310,7 +3239,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (nHeight >= consensusParams.SegwitHeight) {
+ if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != NO_WITNESS_COMMITMENT) {
bool malleated = false;
@@ -3440,7 +3369,6 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
@@ -3448,7 +3376,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
bool accepted = m_blockman.AcceptBlockHeader(
header, state, chainparams, &pindex);
- ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus());
+ ActiveChainstate().CheckBlockIndex();
if (!accepted) {
return false;
@@ -3467,7 +3395,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
@@ -3477,8 +3405,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
- bool accepted_header = m_blockman.AcceptBlockHeader(block, state, chainparams, &pindex);
- CheckBlockIndex(chainparams.GetConsensus());
+ bool accepted_header = m_blockman.AcceptBlockHeader(block, state, m_params, &pindex);
+ CheckBlockIndex();
if (!accepted_header)
return false;
@@ -3515,8 +3443,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
if (pindex->nChainWork < nMinimumChainWork) return true;
}
- if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
- !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
+ if (!CheckBlock(block, state, m_params.GetConsensus()) ||
+ !ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) {
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
@@ -3531,21 +3459,20 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
- FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
+ FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
}
- ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
+ ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
}
- FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
+ FlushStateToDisk(state, FlushStateMode::NONE);
- CheckBlockIndex(chainparams.GetConsensus());
+ CheckBlockIndex();
return true;
}
@@ -3553,7 +3480,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
{
CBlockIndex *pindex = nullptr;
@@ -3572,7 +3498,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
- ret = ActiveChainstate().AcceptBlock(block, state, chainparams, &pindex, force_processing, nullptr, new_block);
+ ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
}
if (!ret) {
GetMainSignals().BlockChecked(*block, state);
@@ -3583,8 +3509,9 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
NotifyHeaderTip(ActiveChainstate());
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
- if (!ActiveChainstate().ActivateBestChain(state, chainparams, block))
+ if (!ActiveChainstate().ActivateBestChain(state, block)) {
return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString());
+ }
return true;
}
@@ -3598,7 +3525,6 @@ bool TestBlockValidity(BlockValidationState& state,
bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
CCoinsViewCache viewNew(&chainstate.CoinsTip());
uint256 block_hash(block.GetHash());
@@ -3608,15 +3534,15 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman));
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
- if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
+ if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, true)) {
return false;
+ }
assert(state.IsValid());
return true;
@@ -3687,10 +3613,8 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
{
BlockValidationState state;
- const CChainParams& chainparams = Params();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.FlushStateToDisk(
- chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
+ state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
@@ -3840,11 +3764,10 @@ void BlockManager::Unload() {
m_block_index.clear();
}
-bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
+bool CChainState::LoadBlockIndexDB()
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!m_blockman.LoadBlockIndex(
- chainparams.GetConsensus(), *pblocktree,
+ m_params.GetConsensus(), *pblocktree,
setBlockIndexCandidates)) {
return false;
}
@@ -3898,14 +3821,14 @@ bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
void CChainState::LoadMempool(const ArgsManager& args)
{
+ if (!m_mempool) return;
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
- ::LoadMempool(m_mempool, *this);
+ ::LoadMempool(*m_mempool, *this);
}
- m_mempool.SetIsLoaded(!ShutdownRequested());
+ m_mempool->SetIsLoaded(!ShutdownRequested());
}
-bool CChainState::LoadChainTip(const CChainParams& chainparams)
+bool CChainState::LoadChainTip()
{
AssertLockHeld(cs_main);
const CCoinsViewCache& coins_cache = CoinsTip();
@@ -3926,10 +3849,10 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams)
tip = m_chain.Tip();
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
- tip->GetBlockHash().ToString(),
- m_chain.Height(),
- FormatISO8601DateTime(tip->GetBlockTime()),
- GuessVerificationProgress(chainparams.TxData(), tip));
+ tip->GetBlockHash().ToString(),
+ m_chain.Height(),
+ FormatISO8601DateTime(tip->GetBlockTime()),
+ GuessVerificationProgress(m_params.TxData(), tip));
return true;
}
@@ -3951,7 +3874,6 @@ bool CVerifyDB::VerifyDB(
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
@@ -4041,8 +3963,9 @@ bool CVerifyDB::VerifyDB(
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!chainstate.ConnectBlock(block, state, pindex, coins)) {
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ }
if (ShutdownRequested()) return true;
}
}
@@ -4054,11 +3977,11 @@ bool CVerifyDB::VerifyDB(
}
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
-bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
+bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs)
{
// TODO: merge with ConnectBlock
CBlock block;
- if (!ReadBlockFromDisk(block, pindex, params.GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) {
return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4074,7 +3997,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
return true;
}
-bool CChainState::ReplayBlocks(const CChainParams& params)
+bool CChainState::ReplayBlocks()
{
LOCK(cs_main);
@@ -4110,7 +4033,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
while (pindexOld != pindexFork) {
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
CBlock block;
- if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pindexOld, m_params.GetConsensus())) {
return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
}
LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);
@@ -4132,7 +4055,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
- if (!RollforwardBlock(pindex, cache, params)) return false;
+ if (!RollforwardBlock(pindex, cache)) return false;
}
cache.SetBestBlock(pindexNew->GetBlockHash());
@@ -4141,15 +4064,14 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
return true;
}
-bool CChainState::NeedsRedownload(const CChainParams& params) const
+bool CChainState::NeedsRedownload() const
{
AssertLockHeld(cs_main);
- // At and above params.SegwitHeight, segwit consensus rules must be validated
+ // At and above m_params.SegwitHeight, segwit consensus rules must be validated
CBlockIndex* block{m_chain.Tip()};
- const int segwit_height{params.GetConsensus().SegwitHeight};
- while (block != nullptr && block->nHeight >= segwit_height) {
+ while (block != nullptr && DeploymentActiveAt(*block, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
// block is insufficiently validated for a segwit client
return true;
@@ -4179,20 +4101,20 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
nLastBlockFile = 0;
setDirtyBlockIndex.clear();
setDirtyFileInfo.clear();
- versionbitscache.Clear();
+ g_versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
fHavePruned = false;
}
-bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
+bool ChainstateManager::LoadBlockIndex()
{
AssertLockHeld(cs_main);
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams);
+ bool ret = ActiveChainstate().LoadBlockIndexDB();
if (!ret) return false;
needs_init = m_blockman.m_block_index.empty();
}
@@ -4209,7 +4131,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
return true;
}
-bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
+bool CChainState::LoadGenesisBlock()
{
LOCK(cs_main);
@@ -4217,17 +4139,16 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
// m_blockman.m_block_index. Note that we can't use m_chain here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
- if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
+ if (m_blockman.m_block_index.count(m_params.GenesisBlock().GetHash()))
return true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
- const CBlock& block = chainparams.GenesisBlock();
- FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
+ const CBlock& block = m_params.GenesisBlock();
+ FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, m_params, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
- ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
+ ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
}
@@ -4235,7 +4156,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
return true;
}
-void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
+void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
@@ -4256,11 +4177,12 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
try {
// locate a header
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
- blkdat.FindByte(chainparams.MessageStart()[0]);
+ blkdat.FindByte(m_params.MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> buf;
- if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE))
+ if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
continue;
+ }
// read size
blkdat >> nSize;
if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE)
@@ -4284,8 +4206,7 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
- if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
+ if (hash != m_params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
if (dbp)
@@ -4294,32 +4215,28 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
// process in case the block isn't known yet
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
- if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
+ if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
if (state.IsError()) {
break;
}
- } else if (hash != chainparams.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
- LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
+ } else if (hash != m_params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
+ LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
}
}
// Activate the genesis block so normal node progress can continue
- if (hash == chainparams.GetConsensus().hashGenesisBlock) {
+ if (hash == m_params.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
- if (!ActivateBestChain(state, chainparams, nullptr)) {
+ if (!ActivateBestChain(state, nullptr)) {
break;
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
// Recursively process earlier encountered successors of this block
@@ -4332,22 +4249,18 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
while (range.first != range.second) {
std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
- if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus()))
- {
+ if (ReadBlockFromDisk(*pblockrecursive, it->second, m_params.GetConsensus())) {
LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(),
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
- if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
- {
+ if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr)) {
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
}
}
range.first++;
mapBlocksUnknownParent.erase(it);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
}
}
@@ -4361,7 +4274,7 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
}
-void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
+void CChainState::CheckBlockIndex()
{
if (!fCheckBlockIndex) {
return;
@@ -4415,7 +4328,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// Begin: actual consistency checks.
if (pindex->pprev == nullptr) {
// Genesis block checks.
- assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
+ assert(pindex->GetBlockHash() == m_params.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
}
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
@@ -4571,16 +4484,14 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
this->ToString(), coinstip_size * (1.0 / 1024 / 1024));
BlockValidationState state;
- const CChainParams& chainparams = Params();
-
bool ret;
if (coinstip_size > old_coinstip_size) {
// Likely no need to flush if cache sizes have grown.
- ret = FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED);
+ ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED);
} else {
// Otherwise, flush state to disk and deallocate the in-memory coins map.
- ret = FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
+ ret = FlushStateToDisk(state, FlushStateMode::ALWAYS);
CoinsTip().ReallocateCache();
}
return ret;
@@ -4628,7 +4539,6 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
}
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
@@ -4779,7 +4689,8 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash)
+CChainState& ChainstateManager::InitializeChainstate(
+ CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
{
bool is_snapshot = snapshot_blockhash.has_value();
std::unique_ptr<CChainState>& to_modify =
@@ -4858,7 +4769,7 @@ bool ChainstateManager::ActivateSnapshot(
}
auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
- this->ActiveChainstate().m_mempool, m_blockman, base_blockhash));
+ /* mempool */ nullptr, m_blockman, base_blockhash));
{
LOCK(::cs_main);
@@ -4881,7 +4792,7 @@ bool ChainstateManager::ActivateSnapshot(
LOCK(::cs_main);
assert(!m_snapshot_chainstate);
m_snapshot_chainstate.swap(snapshot_chainstate);
- const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(::Params());
+ const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
assert(chaintip_loaded);
m_active_chainstate = m_snapshot_chainstate.get();
@@ -4944,6 +4855,14 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
coins_count - coins_left);
return false;
}
+ if (coin.nHeight > base_height ||
+ outpoint.n >= std::numeric_limits<decltype(outpoint.n)>::max() // Avoid integer wrap-around in coinstats.cpp:ApplyHash
+ ) {
+ LogPrintf("[snapshot] bad snapshot data after deserializing %d coins\n",
+ coins_count - coins_left);
+ return false;
+ }
+
coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
--coins_left;
@@ -4966,7 +4885,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
}
const auto snapshot_cache_state = WITH_LOCK(::cs_main,
- return snapshot_chainstate.GetCoinsCacheSizeState(&snapshot_chainstate.m_mempool));
+ return snapshot_chainstate.GetCoinsCacheSizeState());
if (snapshot_cache_state >=
CoinsCacheSizeState::CRITICAL) {
@@ -5055,7 +4974,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
// won't ask to rewind the entire assumed-valid chain on startup.
- if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) {
+ if (index->pprev && DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
}
diff --git a/src/validation.h b/src/validation.h
index 0c396c92ee..9a2be3ad97 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -24,7 +24,6 @@
#include <sync.h>
#include <txmempool.h> // For CTxMemPool::cs
#include <txdb.h>
-#include <versionbits.h>
#include <serialize.h>
#include <util/check.h>
#include <util/hasher.h>
@@ -345,10 +344,6 @@ bool TestBlockValidity(BlockValidationState& state,
bool fCheckPOW = true,
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Check whether witness commitments are required for a block, and whether to enforce NULLDUMMY (BIP 147) rules.
- * Note that transaction witness validation rules are always enforced when P2SH is enforced. */
-bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
-
/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
@@ -592,8 +587,11 @@ protected:
*/
mutable std::atomic<bool> m_cached_finished_ibd{false};
- //! mempool that is kept in sync with the chain
- CTxMemPool& m_mempool;
+ //! Optional mempool that is kept in sync with the chain.
+ //! Only the active chainstate has a mempool.
+ CTxMemPool* m_mempool;
+
+ const CChainParams& m_params;
//! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
std::unique_ptr<CoinsViews> m_coins_views;
@@ -603,7 +601,10 @@ public:
//! CChainState instances.
BlockManager& m_blockman;
- explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash = std::nullopt);
+ explicit CChainState(
+ CTxMemPool* mempool,
+ BlockManager& blockman,
+ std::optional<uint256> from_snapshot_blockhash = std::nullopt);
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -680,7 +681,7 @@ public:
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/** Import blocks from an external file */
- void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr);
+ void LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp = nullptr);
/**
* Update the on-disk chain state.
@@ -694,8 +695,7 @@ public:
* @returns true unless a system error occurred
*/
bool FlushStateToDisk(
- const CChainParams& chainparams,
- BlockValidationState &state,
+ BlockValidationState& state,
FlushStateMode mode,
int nManualPruneHeight = 0);
@@ -723,37 +723,36 @@ public:
*/
bool ActivateBestChain(
BlockValidationState& state,
- const CChainParams& chainparams,
std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(cs_main);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
bool ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CCoinsViewCache& view, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Apply the effects of a block disconnection on the UTXO set.
- bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
+ bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
// Manual block validity manipulation:
/** Mark a block as precious and reorganize.
*
* May not be called in a validationinterface callback.
*/
- bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
- bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Replay blocks that aren't fully applied to the database. */
- bool ReplayBlocks(const CChainParams& params);
+ bool ReplayBlocks();
/** Whether the chain state needs to be redownloaded due to lack of witness data */
- [[nodiscard]] bool NeedsRedownload(const CChainParams& params) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ [[nodiscard]] bool NeedsRedownload() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
- bool LoadGenesisBlock(const CChainParams& chainparams);
+ bool LoadGenesisBlock();
void PruneBlockIndexCandidates();
@@ -767,41 +766,66 @@ public:
*
* By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
*/
- void CheckBlockIndex(const Consensus::Params& consensusParams);
+ void CheckBlockIndex();
/** Load the persisted mempool from disk */
void LoadMempool(const ArgsManager& args);
/** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */
- bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool LoadChainTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Dictates whether we need to flush the cache to disk or not.
//!
//! @return the state of the size of the coins cache.
- CoinsCacheSizeState GetCoinsCacheSizeState(const CTxMemPool* tx_pool)
- EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ CoinsCacheSizeState GetCoinsCacheSizeState() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CoinsCacheSizeState GetCoinsCacheSizeState(
- const CTxMemPool* tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
- bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
- bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
+ bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
+ bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
- void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Indirection necessary to make lock annotations work with an optional mempool.
+ RecursiveMutex* MempoolMutex() const LOCK_RETURNED(m_mempool->cs)
+ {
+ return m_mempool ? &m_mempool->cs : nullptr;
+ }
+
+ /**
+ * Make mempool consistent after a reorg, by re-adding or recursively erasing
+ * disconnected block transactions from the mempool, and also removing any
+ * other transactions from the mempool that are no longer valid given the new
+ * tip/height.
+ *
+ * Note: we assume that disconnectpool only contains transactions that are NOT
+ * confirmed in the current chain nor already in the mempool (otherwise,
+ * in-mempool descendants of such transactions would be removed).
+ *
+ * Passing fAddToMempool=false will skip trying to add the transactions back,
+ * and instead just erase from the mempool as needed.
+ */
+ void MaybeUpdateMempoolForReorg(
+ DisconnectedBlockTransactions& disconnectpool,
+ bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
+
+ /** Check warning conditions and do some notifications on new chain tip set. */
+ void UpdateTip(const CBlockIndex* pindexNew)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
friend ChainstateManager;
};
@@ -891,10 +915,6 @@ private:
CAutoFile& coins_file,
const SnapshotMetadata& metadata);
- // For access to m_active_chainstate.
- friend CChainState& ChainstateActive();
- friend CChain& ChainActive();
-
public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
@@ -916,7 +936,9 @@ public:
// constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
- CChainState& InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash = std::nullopt)
+ CChainState& InitializeChainstate(
+ CTxMemPool* mempool,
+ const std::optional<uint256>& snapshot_blockhash = std::nullopt)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
@@ -1006,7 +1028,7 @@ public:
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
- bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Unload block index and chain data before shutdown.
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
@@ -1017,27 +1039,17 @@ public:
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-};
-
-/** DEPRECATED! Please use node.chainman instead. May only be used in validation.cpp internally */
-extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
-
-/** Please prefer the identical ChainstateManager::ActiveChainstate */
-CChainState& ChainstateActive();
-/** Please prefer the identical ChainstateManager::ActiveChain */
-CChain& ChainActive();
+ ~ChainstateManager() {
+ LOCK(::cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, *this);
+ Reset();
+ }
+};
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
-extern VersionBitsCache versionbitscache;
-
-/**
- * Determine what nVersion a new block should use.
- */
-int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
-
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
/** Dump the mempool to disk. */
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index df2ec4e056..94c3c9559f 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -190,29 +190,48 @@ public:
} // namespace
-ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
+ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
{
- return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
+ LOCK(m_mutex);
+ return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
}
-BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
+BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
{
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params);
}
-int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
+int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
{
- return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]);
+ LOCK(m_mutex);
+ return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, m_caches[pos]);
}
-uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos)
+uint32_t VersionBitsCache::Mask(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
return VersionBitsConditionChecker(pos).Mask(params);
}
+int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
+{
+ LOCK(m_mutex);
+ int32_t nVersion = VERSIONBITS_TOP_BITS;
+
+ for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
+ Consensus::DeploymentPos pos = static_cast<Consensus::DeploymentPos>(i);
+ ThresholdState state = VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
+ if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) {
+ nVersion |= Mask(params, pos);
+ }
+ }
+
+ return nVersion;
+}
+
void VersionBitsCache::Clear()
{
+ LOCK(m_mutex);
for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) {
- caches[d].clear();
+ m_caches[d].clear();
}
}
diff --git a/src/versionbits.h b/src/versionbits.h
index 634a848ef5..0b2f4a0258 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -6,6 +6,8 @@
#define BITCOIN_VERSIONBITS_H
#include <chain.h>
+#include <sync.h>
+
#include <map>
/** What block version to use for new blocks (pre versionbits) */
@@ -71,21 +73,31 @@ public:
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
};
-/** BIP 9 allows multiple softforks to be deployed in parallel. We cache per-period state for every one of them
- * keyed by the bit position used to signal support. */
-struct VersionBitsCache
+/** BIP 9 allows multiple softforks to be deployed in parallel. We cache
+ * per-period state for every one of them. */
+class VersionBitsCache
{
- ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS];
+private:
+ Mutex m_mutex;
+ ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex);
+
+public:
+ /** Get the numerical statistics for a given deployment for the signalling period that includes the block after pindexPrev. */
+ static BIP9Stats Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+
+ static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
+
+ /** Get the BIP9 state for a given deployment for the block after pindexPrev. */
+ ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+
+ /** Get the block height at which the BIP9 deployment switched into the state for the block after pindexPrev. */
+ int StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+
+ /** Determine what nVersion a new block should use
+ */
+ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
void Clear();
};
-/** Get the BIP9 state for a given deployment at the current tip. */
-ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
-/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
-BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
-/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
-int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
-uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
#endif // BITCOIN_VERSIONBITS_H
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
deleted file mode 100644
index fa41bad46d..0000000000
--- a/src/versionbitsinfo.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2016-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 <versionbitsinfo.h>
-
-#include <consensus/params.h>
-
-const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
- {
- /*.name =*/ "testdummy",
- /*.gbt_force =*/ true,
- },
- {
- /*.name =*/ "taproot",
- /*.gbt_force =*/ true,
- },
-};
diff --git a/src/versionbitsinfo.h b/src/versionbitsinfo.h
deleted file mode 100644
index a7822bc747..0000000000
--- a/src/versionbitsinfo.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2016-2018 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_VERSIONBITSINFO_H
-#define BITCOIN_VERSIONBITSINFO_H
-
-struct VBDeploymentInfo {
- /** Deployment name */
- const char *name;
- /** Whether GBT clients can safely ignore this rule in simplified usage */
- bool gbt_force;
-};
-
-extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[];
-
-#endif // BITCOIN_VERSIONBITSINFO_H
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index fe2c810afa..efef1ec754 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -13,8 +13,6 @@
#include <utility>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
{
LOCK(cs_desc_man);
@@ -62,10 +60,10 @@ bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, c
}
// If sign is true, transaction must previously have been filled
-TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (!sign) {
- return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
+ return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed);
}
// Already complete if every input is now signed
@@ -84,5 +82,3 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
FinalizePSBT(psbt); // This won't work in a multisig setup
return TransactionError::OK;
}
-
-#endif
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index 1786958912..61df3d0015 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
#define BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
-#ifdef ENABLE_EXTERNAL_SIGNER
#include <wallet/scriptpubkeyman.h>
#include <memory>
@@ -16,8 +15,8 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
: DescriptorScriptPubKeyMan(storage, descriptor)
{}
- ExternalSignerScriptPubKeyMan(WalletStorage& storage, bool internal)
- : DescriptorScriptPubKeyMan(storage, internal)
+ ExternalSignerScriptPubKeyMan(WalletStorage& storage)
+ : DescriptorScriptPubKeyMan(storage)
{}
/** Provide a descriptor at setup time
@@ -29,8 +28,6 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
};
-#endif
-
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index ee92316b89..e33adf94c9 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -349,9 +349,9 @@ public:
TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) override
+ bool& complete) override
{
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
@@ -475,13 +475,13 @@ public:
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{
return MakeHandler(m_wallet->NotifyAddressBookChanged.connect(
- [fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine,
- const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
+ [fn](const CTxDestination& address, const std::string& label, bool is_mine,
+ const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
}
std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
{
return MakeHandler(m_wallet->NotifyTransactionChanged.connect(
- [fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); }));
+ [fn](const uint256& txid, ChangeType status) { fn(txid, status); }));
}
std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override
{
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 726b13beac..ea97b339cf 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -286,6 +286,9 @@ RPCHelpMan importaddress()
if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
}
+ if (OutputTypeFromDestination(dest) == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
+ }
pwallet->MarkDirty();
@@ -962,6 +965,9 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
}
+ if (OutputTypeFromDestination(dest) == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
+ }
script = GetScriptForDestination(dest);
} else {
if (!IsHex(output)) {
@@ -1086,6 +1092,9 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
if (!parsed_desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
+ if (parsed_desc->GetOutputType() == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets");
+ }
have_solving_data = parsed_desc->IsSolvable();
const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
@@ -1530,6 +1539,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
}
+ // Taproot descriptors cannot be imported if Taproot is not yet active.
+ // Check if this is a Taproot descriptor
+ CTxDestination dest;
+ ExtractDestination(scripts[0], dest);
+ if (std::holds_alternative<WitnessV1Taproot>(dest)) {
+ // Check if Taproot is active
+ if (!wallet.chain().isTaprootActive()) {
+ // Taproot is not active, raise an error
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
+ }
+ }
+
// If private keys are enabled, check some things.
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
@@ -1545,9 +1566,8 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
// Check if the wallet already contains the descriptor
auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
if (existing_spk_manager) {
- LOCK(existing_spk_manager->cs_desc_man);
- if (range_start > existing_spk_manager->GetWalletDescriptor().range_start) {
- throw JSONRPCError(RPC_INVALID_PARAMS, strprintf("range_start can only decrease; current range = [%d,%d]", existing_spk_manager->GetWalletDescriptor().range_start, existing_spk_manager->GetWalletDescriptor().range_end));
+ if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, error);
}
}
@@ -1564,16 +1584,16 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
} else {
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
}
+ } else {
+ if (w_desc.descriptor->GetOutputType()) {
+ wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
+ }
}
result.pushKV("success", UniValue(true));
} catch (const UniValue& e) {
result.pushKV("success", UniValue(false));
result.pushKV("error", e);
- } catch (...) {
- result.pushKV("success", UniValue(false));
-
- result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
}
if (warnings.size()) result.pushKV("warnings", warnings);
return result;
@@ -1766,8 +1786,6 @@ RPCHelpMan listdescriptors()
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
}
- EnsureWalletIsUnlocked(*wallet);
-
LOCK(wallet->cs_wallet);
UniValue descriptors(UniValue::VARR);
@@ -1781,7 +1799,7 @@ RPCHelpMan listdescriptors()
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
std::string descriptor;
- if (!desc_spk_man->GetDescriptorString(descriptor, false)) {
+ if (!desc_spk_man->GetDescriptorString(descriptor)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string.");
}
spk.pushKV("desc", descriptor);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 534c974178..f1d5117415 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -269,6 +269,9 @@ static RPCHelpMan getnewaddress()
if (!ParseOutputType(request.params[1].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
}
+ if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
+ }
}
CTxDestination dest;
@@ -313,6 +316,9 @@ static RPCHelpMan getrawchangeaddress()
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
}
+ if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
+ }
}
CTxDestination dest;
@@ -1004,6 +1010,9 @@ static RPCHelpMan addmultisigaddress()
if (!ParseOutputType(request.params[3].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
}
+ if (output_type == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m multisig addresses cannot be created with legacy wallets");
+ }
}
// Construct using pay-to-script-hash:
@@ -3320,7 +3329,8 @@ RPCHelpMan signrawtransactionwithwallet()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -3542,7 +3552,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
} else {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false /* sign */, true /* bip32derivs */);
CHECK_NONFATAL(err == TransactionError::OK);
CHECK_NONFATAL(!complete);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -3847,17 +3857,22 @@ RPCHelpMan getaddressinfo()
isminetype mine = pwallet->IsMine(dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
- bool solvable = provider && IsSolvable(*provider, scriptPubKey);
- ret.pushKV("solvable", solvable);
-
- if (solvable) {
- ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
+ if (provider) {
+ auto inferred = InferDescriptor(scriptPubKey, *provider);
+ bool solvable = inferred->IsSolvable() || IsSolvable(*provider, scriptPubKey);
+ ret.pushKV("solvable", solvable);
+ if (solvable) {
+ ret.pushKV("desc", inferred->ToString());
+ }
+ } else {
+ ret.pushKV("solvable", false);
}
+
DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey));
if (desc_spk_man) {
std::string desc_str;
- if (desc_spk_man->GetDescriptorString(desc_str, false)) {
+ if (desc_spk_man->GetDescriptorString(desc_str)) {
ret.pushKV("parent_desc", desc_str);
}
}
@@ -4175,8 +4190,8 @@ static RPCHelpMan send()
// First fill transaction with our data without signing,
// so external signers are not asked sign more than once.
bool complete;
- pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
+ pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false, true);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, true, false);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4291,7 +4306,8 @@ static RPCHelpMan walletprocesspsbt()
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
{"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -4315,6 +4331,11 @@ static RPCHelpMan walletprocesspsbt()
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
+ const CWallet& wallet{*pwallet};
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ wallet.BlockUntilSyncedToCurrentChain();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
// Unserialize the transaction
@@ -4331,7 +4352,7 @@ static RPCHelpMan walletprocesspsbt()
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, nHashType, sign, bip32derivs);
+ const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4429,6 +4450,11 @@ static RPCHelpMan walletcreatefundedpsbt()
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
+ CWallet& wallet{*pwallet};
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ wallet.BlockUntilSyncedToCurrentChain();
+
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -4440,7 +4466,7 @@ static RPCHelpMan walletcreatefundedpsbt()
CAmount fee;
int change_position;
- bool rbf = pwallet->m_signal_rbf;
+ bool rbf{wallet.m_signal_rbf};
const UniValue &replaceable_arg = request.params[3]["replaceable"];
if (!replaceable_arg.isNull()) {
RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
@@ -4451,7 +4477,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(*pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
+ FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
@@ -4459,7 +4485,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
bool complete = true;
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, 1, false, bip32derivs);
+ const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 2eb9ca5c6d..73433214f1 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -13,7 +13,6 @@
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
-#include <external_signer.h>
#include <wallet/scriptpubkeyman.h>
#include <optional>
@@ -23,6 +22,12 @@ const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
+ if (LEGACY_OUTPUT_TYPES.count(type) == 0) {
+ error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types").translated;
+ return false;
+ }
+ assert(type != OutputType::BECH32M);
+
LOCK(cs_KeyStore);
error.clear();
@@ -290,14 +295,22 @@ bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBat
return true;
}
-bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool)
+bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error)
{
+ if (LEGACY_OUTPUT_TYPES.count(type) == 0) {
+ error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types").translated;
+ return false;
+ }
+ assert(type != OutputType::BECH32M);
+
LOCK(cs_KeyStore);
if (!CanGetAddresses(internal)) {
+ error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
return false;
}
if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
+ error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
return false;
}
address = GetDestinationForKey(keypool.vchPubKey, type);
@@ -597,7 +610,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
return SigningResult::SIGNING_FAILED;
}
-TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -626,7 +639,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
}
SignatureData sigdata;
input.FillSignatureData(sigdata);
- SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
@@ -1295,6 +1308,7 @@ void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const
void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type)
{
+ assert(type != OutputType::BECH32M);
// Remove from key pool
WalletBatch batch(m_storage.GetDatabase());
batch.ErasePool(nIndex);
@@ -1328,6 +1342,7 @@ void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, co
bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type, bool internal)
{
+ assert(type != OutputType::BECH32M);
if (!CanGetAddresses(internal)) {
return false;
}
@@ -1396,6 +1411,7 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key
void LegacyScriptPubKeyMan::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
+ assert(type != OutputType::BECH32M);
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
CTxDestination witdest = WitnessV0KeyHash(key.GetID());
CScript witprog = GetScriptForDestination(witdest);
@@ -1597,12 +1613,10 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
return set_address;
}
-void LegacyScriptPubKeyMan::SetInternal(bool internal) {}
-
bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
// Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later
- if (!CanGetAddresses(m_internal)) {
+ if (!CanGetAddresses()) {
error = "No addresses available";
return false;
}
@@ -1707,10 +1721,9 @@ bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, Walle
return true;
}
-bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool)
+bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error)
{
LOCK(cs_desc_man);
- std::string error;
bool result = GetNewDestination(type, address, error);
index = m_wallet_descriptor.next_index - 1;
return result;
@@ -1790,34 +1803,10 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
}
m_map_pubkeys[pubkey] = i;
}
- // Write the cache
- for (const auto& parent_xpub_pair : temp_cache.GetCachedParentExtPubKeys()) {
- CExtPubKey xpub;
- if (m_wallet_descriptor.cache.GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) {
- if (xpub != parent_xpub_pair.second) {
- throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub");
- }
- continue;
- }
- if (!batch.WriteDescriptorParentCache(parent_xpub_pair.second, id, parent_xpub_pair.first)) {
- throw std::runtime_error(std::string(__func__) + ": writing cache item failed");
- }
- m_wallet_descriptor.cache.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second);
- }
- for (const auto& derived_xpub_map_pair : temp_cache.GetCachedDerivedExtPubKeys()) {
- for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
- CExtPubKey xpub;
- if (m_wallet_descriptor.cache.GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) {
- if (xpub != derived_xpub_pair.second) {
- throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub");
- }
- continue;
- }
- if (!batch.WriteDescriptorDerivedCache(derived_xpub_pair.second, id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
- throw std::runtime_error(std::string(__func__) + ": writing cache item failed");
- }
- m_wallet_descriptor.cache.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second);
- }
+ // Merge and write the cache
+ DescriptorCache new_items = m_wallet_descriptor.cache.MergeAndDiff(temp_cache);
+ if (!batch.WriteDescriptorCacheItems(id, new_items)) {
+ throw std::runtime_error(std::string(__func__) + ": writing cache items failed");
}
m_max_cached_index++;
}
@@ -1860,6 +1849,12 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
AssertLockHeld(cs_desc_man);
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ // Check if provided key already exists
+ if (m_map_keys.find(pubkey.GetID()) != m_map_keys.end() ||
+ m_map_crypted_keys.find(pubkey.GetID()) != m_map_crypted_keys.end()) {
+ return true;
+ }
+
if (m_storage.HasEncryptionKeys()) {
if (m_storage.IsLocked()) {
return false;
@@ -1879,8 +1874,14 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
}
}
-bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type)
+bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type, bool internal)
{
+ if (addr_type == OutputType::BECH32M) {
+ // Don't allow setting up taproot descriptors yet
+ // TODO: Allow setting up taproot descriptors
+ return false;
+ }
+
LOCK(cs_desc_man);
assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
@@ -1910,6 +1911,7 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
desc_prefix = "wpkh(" + xpub + "/84'";
break;
}
+ case OutputType::BECH32M: assert(false); // TODO: Setup taproot descriptor
} // no default case, so the compiler can warn about missing cases
assert(!desc_prefix.empty());
@@ -1920,7 +1922,7 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
desc_prefix += "/0'";
}
- std::string internal_path = m_internal ? "/1" : "/0";
+ std::string internal_path = internal ? "/1" : "/0";
std::string desc_str = desc_prefix + "/0'" + internal_path + desc_suffix;
// Make the descriptor
@@ -1975,13 +1977,6 @@ int64_t DescriptorScriptPubKeyMan::GetOldestKeyPoolTime() const
return 0;
}
-size_t DescriptorScriptPubKeyMan::KeypoolCountExternalKeys() const
-{
- if (m_internal) {
- return 0;
- }
- return GetKeyPoolSize();
-}
unsigned int DescriptorScriptPubKeyMan::GetKeyPoolSize() const
{
@@ -2083,7 +2078,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -2133,7 +2128,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
}
}
- SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
@@ -2183,11 +2178,6 @@ uint256 DescriptorScriptPubKeyMan::GetID() const
return id;
}
-void DescriptorScriptPubKeyMan::SetInternal(bool internal)
-{
- this->m_internal = internal;
-}
-
void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache)
{
LOCK(cs_desc_man);
@@ -2268,15 +2258,75 @@ const std::vector<CScript> DescriptorScriptPubKeyMan::GetScriptPubKeys() const
return script_pub_keys;
}
-bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, bool priv) const
+bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out) const
{
LOCK(cs_desc_man);
- if (m_storage.IsLocked()) {
- return false;
+
+ FlatSigningProvider provider;
+ provider.keys = GetKeys();
+
+ return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache);
+}
+
+void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()
+{
+ LOCK(cs_desc_man);
+ if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {
+ return;
}
+ // Skip if we have the last hardened xpub cache
+ if (m_wallet_descriptor.cache.GetCachedLastHardenedExtPubKeys().size() > 0) {
+ return;
+ }
+
+ // Expand the descriptor
FlatSigningProvider provider;
provider.keys = GetKeys();
+ FlatSigningProvider out_keys;
+ std::vector<CScript> scripts_temp;
+ DescriptorCache temp_cache;
+ if (!m_wallet_descriptor.descriptor->Expand(0, provider, scripts_temp, out_keys, &temp_cache)){
+ throw std::runtime_error("Unable to expand descriptor");
+ }
+
+ // Cache the last hardened xpubs
+ DescriptorCache diff = m_wallet_descriptor.cache.MergeAndDiff(temp_cache);
+ if (!WalletBatch(m_storage.GetDatabase()).WriteDescriptorCacheItems(GetID(), diff)) {
+ throw std::runtime_error(std::string(__func__) + ": writing cache items failed");
+ }
+}
- return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, priv);
+void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor)
+{
+ LOCK(cs_desc_man);
+ std::string error;
+ if (!CanUpdateToWalletDescriptor(descriptor, error)) {
+ throw std::runtime_error(std::string(__func__) + ": " + error);
+ }
+
+ m_map_pubkeys.clear();
+ m_map_script_pub_keys.clear();
+ m_max_cached_index = -1;
+ m_wallet_descriptor = descriptor;
+}
+
+bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error)
+{
+ LOCK(cs_desc_man);
+ if (!HasWalletDescriptor(descriptor)) {
+ error = "can only update matching descriptor";
+ return false;
+ }
+
+ if (descriptor.range_start > m_wallet_descriptor.range_start ||
+ descriptor.range_end < m_wallet_descriptor.range_end) {
+ // Use inclusive range for error
+ error = strprintf("new range must include current range = [%d,%d]",
+ m_wallet_descriptor.range_start,
+ m_wallet_descriptor.range_end - 1);
+ return false;
+ }
+
+ return true;
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index b8e34fbac3..572a695662 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -181,7 +181,7 @@ public:
virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) { return false; }
virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; }
- virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { return false; }
+ virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) { return false; }
virtual void KeepDestination(int64_t index, const OutputType& type) {}
virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {}
@@ -216,7 +216,6 @@ public:
virtual int64_t GetOldestKeyPoolTime() const { return GetTime(); }
- virtual size_t KeypoolCountExternalKeys() const { return 0; }
virtual unsigned int GetKeyPoolSize() const { return 0; }
virtual int64_t GetTimeFirstKey() const { return 0; }
@@ -235,12 +234,10 @@ public:
/** Sign a message with the given script */
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
- virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
virtual uint256 GetID() const { return uint256(); }
- virtual void SetInternal(bool internal) {}
-
/** Prepends the wallet name in logging output to ease debugging in multi-wallet use cases */
template<typename... Params>
void WalletLogPrintf(std::string fmt, Params... parameters) const {
@@ -254,6 +251,13 @@ public:
boost::signals2::signal<void ()> NotifyCanGetAddressesChanged;
};
+/** OutputTypes supported by the LegacyScriptPubKeyMan */
+static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES {
+ OutputType::LEGACY,
+ OutputType::P2SH_SEGWIT,
+ OutputType::BECH32,
+};
+
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
{
private:
@@ -357,7 +361,7 @@ public:
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
- bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override;
+ bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) override;
void KeepDestination(int64_t index, const OutputType& type) override;
void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override;
@@ -379,7 +383,7 @@ public:
void RewriteDB() override;
int64_t GetOldestKeyPoolTime() const override;
- size_t KeypoolCountExternalKeys() const override;
+ size_t KeypoolCountExternalKeys() const;
unsigned int GetKeyPoolSize() const override;
int64_t GetTimeFirstKey() const override;
@@ -394,12 +398,10 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
- void SetInternal(bool internal) override;
-
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
@@ -526,8 +528,6 @@ private:
PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man);
int32_t m_max_cached_index = -1;
- bool m_internal = false;
-
KeyMap m_map_keys GUARDED_BY(cs_desc_man);
CryptedKeyMap m_map_crypted_keys GUARDED_BY(cs_desc_man);
@@ -553,9 +553,8 @@ public:
: ScriptPubKeyMan(storage),
m_wallet_descriptor(descriptor)
{}
- DescriptorScriptPubKeyMan(WalletStorage& storage, bool internal)
- : ScriptPubKeyMan(storage),
- m_internal(internal)
+ DescriptorScriptPubKeyMan(WalletStorage& storage)
+ : ScriptPubKeyMan(storage)
{}
mutable RecursiveMutex cs_desc_man;
@@ -566,7 +565,7 @@ public:
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
- bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override;
+ bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) override;
void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override;
// Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file
@@ -580,7 +579,7 @@ public:
bool IsHDEnabled() const override;
//! Setup descriptors based on the given CExtkey
- bool SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type);
+ bool SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type, bool internal);
/** Provide a descriptor at setup time
* Returns false if already setup or setup fails, true if setup is successful
@@ -590,7 +589,6 @@ public:
bool HavePrivateKeys() const override;
int64_t GetOldestKeyPoolTime() const override;
- size_t KeypoolCountExternalKeys() const override;
unsigned int GetKeyPoolSize() const override;
int64_t GetTimeFirstKey() const override;
@@ -605,25 +603,27 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
- void SetInternal(bool internal) override;
-
void SetCache(const DescriptorCache& cache);
bool AddKey(const CKeyID& key_id, const CKey& key);
bool AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key);
bool HasWalletDescriptor(const WalletDescriptor& desc) const;
+ void UpdateWalletDescriptor(WalletDescriptor& descriptor);
+ bool CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error);
void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
void WriteDescriptor();
const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
const std::vector<CScript> GetScriptPubKeys() const;
- bool GetDescriptorString(std::string& out, bool priv) const;
+ bool GetDescriptorString(std::string& out) const;
+
+ void UpgradeDescriptorCache();
};
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index c8ded4c51e..6a8df437ae 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -618,8 +618,9 @@ bool CWallet::CreateTransactionInternal(
// Reserve a new key pair from key pool. If it fails, provide a dummy
// destination in case we don't need change.
CTxDestination dest;
- if (!reservedest.GetReservedDestination(dest, true)) {
- error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
+ std::string dest_err;
+ if (!reservedest.GetReservedDestination(dest, true, dest_err)) {
+ error = strprintf(_("Transaction needs a change address, but we can't generate it. %s"), dest_err);
}
scriptChange = GetScriptForDestination(dest);
// A valid destination implies a change script (and
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index ce7e661b67..1cefa386b7 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
+ BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 6a791748b4..a0070b8dd3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -83,17 +83,17 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -163,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -180,10 +180,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
{
@@ -242,7 +242,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -265,7 +265,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
JSONRPCRequest request;
request.params.setArray();
@@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
RemoveWallet(wallet, std::nullopt);
@@ -313,9 +313,9 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -483,16 +483,16 @@ public:
wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
wallet->LoadWallet();
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(*wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
BOOST_CHECK(result.last_failed_block.IsNull());
}
@@ -521,10 +521,10 @@ public:
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(wallet->cs_wallet);
- wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4b6630de3c..27565aefc9 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -8,6 +8,7 @@
#include <chain.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
+#include <external_signer.h>
#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
@@ -373,6 +374,19 @@ void CWallet::UpgradeKeyMetadata()
SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
}
+void CWallet::UpgradeDescriptorCache()
+{
+ if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {
+ return;
+ }
+
+ for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) {
+ DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm);
+ desc_spkm->UpgradeDescriptorCache();
+ }
+ SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);
+}
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys)
{
CCrypter crypter;
@@ -389,6 +403,8 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key
if (Unlock(_vMasterKey, accept_no_keys)) {
// Now that we've unlocked, upgrade the key metadata
UpgradeKeyMetadata();
+ // Now that we've unlocked, upgrade the descriptor cache
+ UpgradeDescriptorCache();
return true;
}
}
@@ -800,7 +816,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
success = false;
}
- NotifyTransactionChanged(this, originalHash, CT_UPDATED);
+ NotifyTransactionChanged(originalHash, CT_UPDATED);
return success;
}
@@ -929,7 +945,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
wtx.MarkDirty();
// Notify UI of new or updated transaction
- NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
+ NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED);
#if HAVE_SYSTEM
// notify an external script when a wallet transaction comes in or is updated
@@ -1103,7 +1119,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
wtx.setAbandoned();
wtx.MarkDirty();
batch.WriteTx(wtx);
- NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);
+ NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.hash == now) {
@@ -1807,7 +1823,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
}
std::map<int, std::string> input_errors;
- return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
+ return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);
}
bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
@@ -1830,6 +1846,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
if (n_signed) {
*n_signed = 0;
}
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -1856,7 +1873,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
// Fill in information from ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
int n_signed_this_spkm = 0;
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
+ TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
if (res != TransactionError::OK) {
return res;
}
@@ -1907,7 +1924,13 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
- return OutputType::BECH32;
+ if (GetScriptPubKeyMan(OutputType::BECH32M, true)) {
+ return OutputType::BECH32M;
+ } else if (GetScriptPubKeyMan(OutputType::BECH32, true)) {
+ return OutputType::BECH32;
+ } else {
+ return m_default_address_type;
+ }
}
}
@@ -1936,7 +1959,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
for (const CTxIn& txin : tx->vin) {
CWalletTx &coin = mapWallet.at(txin.prevout.hash);
coin.MarkDirty();
- NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
+ NotifyTransactionChanged(coin.GetHash(), CT_UPDATED);
}
// Get the inserted-CWalletTx from mapWallet so that the
@@ -1991,7 +2014,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
for (const auto& txin : it->second.tx->vin)
mapTxSpends.erase(txin.prevout);
mapWallet.erase(it);
- NotifyTransactionChanged(this, hash, CT_DELETED);
+ NotifyTransactionChanged(hash, CT_DELETED);
}
if (nZapSelectTxRet == DBErrors::NEED_REWRITE)
@@ -2025,8 +2048,8 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].purpose = strPurpose;
is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, strName, is_mine,
- strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
+ NotifyAddressBookChanged(address, strName, is_mine,
+ strPurpose, (fUpdated ? CT_UPDATED : CT_NEW));
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
return batch.WriteName(EncodeDestination(address), strName);
@@ -2061,7 +2084,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
+ NotifyAddressBookChanged(address, "", is_mine, "", CT_DELETED);
batch.ErasePurpose(EncodeDestination(address));
return batch.EraseName(EncodeDestination(address));
@@ -2071,9 +2094,14 @@ size_t CWallet::KeypoolCountExternalKeys() const
{
AssertLockHeld(cs_wallet);
+ auto legacy_spk_man = GetLegacyScriptPubKeyMan();
+ if (legacy_spk_man) {
+ return legacy_spk_man->KeypoolCountExternalKeys();
+ }
+
unsigned int count = 0;
- for (auto spk_man : GetActiveScriptPubKeyMans()) {
- count += spk_man->KeypoolCountExternalKeys();
+ for (auto spk_man : m_external_spk_managers) {
+ count += spk_man.second->GetKeyPoolSize();
}
return count;
@@ -2110,7 +2138,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label,
spk_man->TopUp();
result = spk_man->GetNewDestination(type, dest, error);
} else {
- error = strprintf("Error: No %s addresses available.", FormatOutputType(type));
+ error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type)).translated;
}
if (result) {
SetAddressBook(dest, label, "receive");
@@ -2125,8 +2153,7 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des
error.clear();
ReserveDestination reservedest(this, type);
- if (!reservedest.GetReservedDestination(dest, true)) {
- error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
+ if (!reservedest.GetReservedDestination(dest, true, error)) {
return false;
}
@@ -2173,10 +2200,11 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
return result;
}
-bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal)
+bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal, std::string& error)
{
m_spk_man = pwallet->GetScriptPubKeyMan(type, internal);
if (!m_spk_man) {
+ error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type)).translated;
return false;
}
@@ -2186,7 +2214,7 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool inter
m_spk_man->TopUp();
CKeyPool keypool;
- if (!m_spk_man->GetReservedDestination(type, internal, address, nIndex, keypool)) {
+ if (!m_spk_man->GetReservedDestination(type, internal, address, nIndex, keypool, error)) {
return false;
}
fInternal = keypool.fInternal;
@@ -2215,7 +2243,6 @@ void ReserveDestination::ReturnDestination()
bool CWallet::DisplayAddress(const CTxDestination& dest)
{
-#ifdef ENABLE_EXTERNAL_SIGNER
CScript scriptPubKey = GetScriptForDestination(dest);
const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
if (spk_man == nullptr) {
@@ -2227,9 +2254,6 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
}
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
-#else
- return false;
-#endif
}
void CWallet::LockCoin(const COutPoint& output)
@@ -2958,7 +2982,6 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern
const std::map<OutputType, ScriptPubKeyMan*>& spk_managers = internal ? m_internal_spk_managers : m_external_spk_managers;
std::map<OutputType, ScriptPubKeyMan*>::const_iterator it = spk_managers.find(type);
if (it == spk_managers.end()) {
- WalletLogPrintf("%s scriptPubKey Manager for output type %d does not exist\n", internal ? "Internal" : "External", static_cast<int>(type));
return nullptr;
}
return it->second;
@@ -3035,7 +3058,7 @@ void CWallet::SetupLegacyScriptPubKeyMan()
}
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this));
- for (const auto& type : OUTPUT_TYPES) {
+ for (const auto& type : LEGACY_OUTPUT_TYPES) {
m_internal_spk_managers[type] = spk_manager.get();
m_external_spk_managers[type] = spk_manager.get();
}
@@ -3063,12 +3086,8 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
{
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
-#ifdef ENABLE_EXTERNAL_SIGNER
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif
} else {
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
@@ -3092,7 +3111,12 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
for (bool internal : {false, true}) {
for (OutputType t : OUTPUT_TYPES) {
- auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, internal));
+ if (t == OutputType::BECH32M) {
+ // Skip taproot (bech32m) for now
+ // TODO: Setup taproot (bech32m) descriptors by default
+ continue;
+ }
+ auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this));
if (IsCrypted()) {
if (IsLocked()) {
throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
@@ -3101,14 +3125,13 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
}
}
- spk_manager->SetupDescriptorGeneration(master_key, t);
+ spk_manager->SetupDescriptorGeneration(master_key, t, internal);
uint256 id = spk_manager->GetID();
m_spk_managers[id] = std::move(spk_manager);
AddActiveScriptPubKeyMan(id, t, internal);
}
}
} else {
-#ifdef ENABLE_EXTERNAL_SIGNER
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
// TODO: add account parameter
@@ -3128,16 +3151,13 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
continue;
}
OutputType t = *desc->GetOutputType();
- auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, internal));
+ auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this));
spk_manager->SetupDescriptor(std::move(desc));
uint256 id = spk_manager->GetID();
m_spk_managers[id] = std::move(spk_manager);
AddActiveScriptPubKeyMan(id, t, internal);
}
}
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif // ENABLE_EXTERNAL_SIGNER
}
}
@@ -3152,12 +3172,37 @@ void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool interna
void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
{
+ // Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets.
+ // Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types.
+ Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
+
WalletLogPrintf("Setting spkMan to active: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(type), static_cast<int>(internal));
auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
+ auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers;
auto spk_man = m_spk_managers.at(id).get();
- spk_man->SetInternal(internal);
spk_mans[type] = spk_man;
+ if (spk_mans_other[type] == spk_man) {
+ spk_mans_other.erase(type);
+ }
+
+ NotifyCanGetAddressesChanged();
+}
+
+void CWallet::DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool internal)
+{
+ auto spk_man = GetScriptPubKeyMan(type, internal);
+ if (spk_man != nullptr && spk_man->GetID() == id) {
+ WalletLogPrintf("Deactivate spkMan: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(type), static_cast<int>(internal));
+ WalletBatch batch(GetDatabase());
+ if (!batch.EraseActiveScriptPubKeyMan(static_cast<uint8_t>(type), internal)) {
+ throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed");
+ }
+
+ auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
+ spk_mans.erase(type);
+ }
+
NotifyCanGetAddressesChanged();
}
@@ -3191,44 +3236,26 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
}
LOCK(cs_wallet);
- auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
-
- // If we already have this descriptor, remove it from the maps but add the existing cache to desc
- auto old_spk_man = GetDescriptorScriptPubKeyMan(desc);
- if (old_spk_man) {
+ auto spk_man = GetDescriptorScriptPubKeyMan(desc);
+ if (spk_man) {
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
+ spk_man->UpdateWalletDescriptor(desc);
+ } else {
+ auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
+ spk_man = new_spk_man.get();
- {
- LOCK(old_spk_man->cs_desc_man);
- new_spk_man->SetCache(old_spk_man->GetWalletDescriptor().cache);
- }
-
- // Remove from maps of active spkMans
- auto old_spk_man_id = old_spk_man->GetID();
- for (bool internal : {false, true}) {
- for (OutputType t : OUTPUT_TYPES) {
- auto active_spk_man = GetScriptPubKeyMan(t, internal);
- if (active_spk_man && active_spk_man->GetID() == old_spk_man_id) {
- if (internal) {
- m_internal_spk_managers.erase(t);
- } else {
- m_external_spk_managers.erase(t);
- }
- break;
- }
- }
- }
- m_spk_managers.erase(old_spk_man_id);
+ // Save the descriptor to memory
+ m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
}
// Add the private keys to the descriptor
for (const auto& entry : signing_provider.keys) {
const CKey& key = entry.second;
- new_spk_man->AddDescriptorKey(key, key.GetPubKey());
+ spk_man->AddDescriptorKey(key, key.GetPubKey());
}
// Top up key pool, the manager will generate new scriptPubKeys internally
- if (!new_spk_man->TopUp()) {
+ if (!spk_man->TopUp()) {
WalletLogPrintf("Could not top up scriptPubKeys\n");
return nullptr;
}
@@ -3236,7 +3263,7 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
// Apply the label if necessary
// Note: we disable labels for ranged descriptors
if (!desc.descriptor->IsRange()) {
- auto script_pub_keys = new_spk_man->GetScriptPubKeys();
+ auto script_pub_keys = spk_man->GetScriptPubKeys();
if (script_pub_keys.empty()) {
WalletLogPrintf("Could not generate scriptPubKeys (cache is empty)\n");
return nullptr;
@@ -3248,12 +3275,8 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
}
}
- // Save the descriptor to memory
- auto ret = new_spk_man.get();
- m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
-
// Save the descriptor to DB
- ret->WriteDescriptor();
+ spk_man->WriteDescriptor();
- return ret;
+ return spk_man;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index d0e26c416c..3997751f52 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,7 +21,6 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
-#include <external_signer.h>
#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
#include <wallet/spend.h>
@@ -118,6 +117,7 @@ static constexpr uint64_t KNOWN_WALLET_FLAGS =
WALLET_FLAG_AVOID_REUSE
| WALLET_FLAG_BLANK_WALLET
| WALLET_FLAG_KEY_ORIGIN_METADATA
+ | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED
| WALLET_FLAG_DISABLE_PRIVATE_KEYS
| WALLET_FLAG_DESCRIPTORS
| WALLET_FLAG_EXTERNAL_SIGNER;
@@ -129,6 +129,7 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
{"avoid_reuse", WALLET_FLAG_AVOID_REUSE},
{"blank", WALLET_FLAG_BLANK_WALLET},
{"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA},
+ {"last_hardened_xpub_cached", WALLET_FLAG_LAST_HARDENED_XPUB_CACHED},
{"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS},
{"descriptor_wallet", WALLET_FLAG_DESCRIPTORS},
{"external_signer", WALLET_FLAG_EXTERNAL_SIGNER}
@@ -182,7 +183,7 @@ public:
}
//! Reserve an address
- bool GetReservedDestination(CTxDestination& pubkey, bool internal);
+ bool GetReservedDestination(CTxDestination& pubkey, bool internal, std::string& error);
//! Return reserved address
void ReturnDestination();
//! Keep the address. Do not return it's key to the keypool when this object goes out of scope
@@ -477,6 +478,9 @@ public:
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Upgrade DescriptorCaches
+ void UpgradeDescriptorCache() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; }
//! Adds a destination data tuple to the store, without saving it to disk
@@ -725,19 +729,18 @@ public:
/**
* Address book entry changed.
- * @note called with lock cs_wallet held.
+ * @note called without lock cs_wallet held.
*/
- boost::signals2::signal<void (CWallet *wallet, const CTxDestination
- &address, const std::string &label, bool isMine,
- const std::string &purpose,
- ChangeType status)> NotifyAddressBookChanged;
+ boost::signals2::signal<void(const CTxDestination& address,
+ const std::string& label, bool isMine,
+ const std::string& purpose, ChangeType status)>
+ NotifyAddressBookChanged;
/**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
- boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx,
- ChangeType status)> NotifyTransactionChanged;
+ boost::signals2::signal<void(const uint256& hashTx, ChangeType status)> NotifyTransactionChanged;
/** Show progress e.g. for rescan */
boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;
@@ -896,6 +899,12 @@ public:
//! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
+ //! Remove specified ScriptPubKeyMan from set of active SPK managers. Writes the change to the wallet file.
+ //! @param[in] id The unique id for the ScriptPubKeyMan
+ //! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
+ //! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
+ void DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool internal);
+
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 24d5351945..1e5d8dfa3a 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -52,6 +52,7 @@ const std::string TX{"tx"};
const std::string VERSION{"version"};
const std::string WALLETDESCRIPTOR{"walletdescriptor"};
const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
+const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"};
const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
const std::string WATCHMETA{"watchmeta"};
@@ -209,6 +210,12 @@ bool WalletBatch::WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bo
return WriteIC(make_pair(key, type), id);
}
+bool WalletBatch::EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
+{
+ const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
+ return EraseIC(make_pair(key, type));
+}
+
bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
{
// hash pubkey/privkey to accelerate wallet load
@@ -248,6 +255,35 @@ bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint2
return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
}
+bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
+{
+ std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
+ xpub.Encode(ser_xpub.data());
+ return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
+}
+
+bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
+{
+ for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
+ if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
+ return false;
+ }
+ }
+ for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
+ for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
+ if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
+ return false;
+ }
+ }
+ }
+ for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
+ if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
+ return false;
+ }
+ }
+ return true;
+}
+
class CWalletScanState {
public:
unsigned int nKeys{0};
@@ -602,6 +638,17 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
} else {
wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
}
+ } else if (strType == DBKeys::WALLETDESCRIPTORLHCACHE) {
+ uint256 desc_id;
+ uint32_t key_exp_index;
+ ssKey >> desc_id;
+ ssKey >> key_exp_index;
+
+ std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
+ ssValue >> ser_xpub;
+ CExtPubKey xpub;
+ xpub.Decode(ser_xpub.data());
+ wss.m_descriptor_caches[desc_id].CacheLastHardenedExtPubKey(key_exp_index, xpub);
} else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
uint256 desc_id;
CPubKey pubkey;
@@ -843,6 +890,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
result = DBErrors::CORRUPT;
}
+ // Upgrade all of the descriptor caches to cache the last hardened xpub
+ // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
+ try {
+ pwallet->UpgradeDescriptorCache();
+ } catch (...) {
+ result = DBErrors::CORRUPT;
+ }
+
// Set the inactive chain
if (wss.m_hd_chains.size() > 0) {
LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan();
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index e7b2d7d780..9b775eb481 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -246,6 +246,8 @@ public:
bool WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor);
bool WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index);
bool WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index);
+ bool WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index);
+ bool WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache);
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
@@ -253,6 +255,7 @@ public:
bool EraseDestData(const std::string &address, const std::string &key);
bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal);
+ bool EraseActiveScriptPubKeyMan(uint8_t type, bool internal);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 0713f768c1..c75e1759bc 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -43,6 +43,9 @@ enum WalletFlags : uint64_t {
// Indicates that the metadata has already been upgraded to contain key origins
WALLET_FLAG_KEY_ORIGIN_METADATA = (1ULL << 1),
+ // Indicates that the descriptor cache has been upgraded to cache last hardened xpubs
+ WALLET_FLAG_LAST_HARDENED_XPUB_CACHED = (1ULL << 2),
+
// will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys)
WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32),