aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.qt.include12
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/chainparams.cpp49
-rw-r--r--src/chainparamsbase.cpp1
-rw-r--r--src/consensus/params.h8
-rw-r--r--src/init.cpp89
-rw-r--r--src/interfaces/node.cpp2
-rw-r--r--src/net.cpp62
-rw-r--r--src/net.h40
-rw-r--r--src/net_permissions.cpp107
-rw-r--r--src/net_permissions.h62
-rw-r--r--src/net_processing.cpp49
-rw-r--r--src/node/coin.cpp3
-rw-r--r--src/node/transaction.cpp2
-rw-r--r--src/qt/bitcoin.cpp14
-rw-r--r--src/qt/bitcoin.h2
-rw-r--r--src/qt/bitcoin.qrc10
-rw-r--r--src/qt/bitcoingui.cpp34
-rw-r--r--src/qt/res/icons/about.pngbin3717 -> 0 bytes
-rw-r--r--src/qt/res/icons/about_qt.pngbin2240 -> 0 bytes
-rw-r--r--src/qt/res/icons/configure.pngbin2865 -> 0 bytes
-rw-r--r--src/qt/res/icons/debugwindow.pngbin1327 -> 0 bytes
-rw-r--r--src/qt/res/icons/filesave.pngbin2359 -> 0 bytes
-rw-r--r--src/qt/res/icons/info.pngbin2028 -> 0 bytes
-rw-r--r--src/qt/res/icons/key.pngbin1759 -> 0 bytes
-rw-r--r--src/qt/res/icons/open.pngbin1694 -> 0 bytes
-rw-r--r--src/qt/res/icons/quit.pngbin1091 -> 0 bytes
-rw-r--r--src/qt/res/icons/verify.pngbin2034 -> 0 bytes
-rw-r--r--src/qt/res/src/verify.svg14
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/test/test_main.cpp2
-rw-r--r--src/rest.cpp4
-rw-r--r--src/rpc/blockchain.cpp160
-rw-r--r--src/rpc/mining.cpp5
-rw-r--r--src/rpc/misc.cpp12
-rw-r--r--src/rpc/net.cpp8
-rw-r--r--src/rpc/rawtransaction.cpp6
-rw-r--r--src/rpc/util.cpp5
-rw-r--r--src/script/descriptor.cpp205
-rw-r--r--src/script/descriptor.h10
-rw-r--r--src/test/descriptor_tests.cpp74
-rw-r--r--src/test/miner_tests.cpp6
-rw-r--r--src/test/netbase_tests.cpp79
-rw-r--r--src/test/setup_common.cpp13
-rw-r--r--src/test/txvalidationcache_tests.cpp22
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/txdb.h5
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/error.cpp7
-rw-r--r--src/util/error.h6
-rw-r--r--src/util/system.cpp16
-rw-r--r--src/util/system.h2
-rw-r--r--src/validation.cpp136
-rw-r--r--src/validation.h110
-rw-r--r--src/versionbits.cpp1
-rw-r--r--src/versionbits.h28
-rw-r--r--src/versionbitsinfo.cpp8
-rw-r--r--src/wallet/rpcdump.cpp5
-rw-r--r--src/wallet/rpcwallet.cpp69
62 files changed, 1071 insertions, 508 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ef5b1900d9..141d8e68ea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -150,6 +150,7 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
+ net_permissions.h \
net_processing.h \
netaddress.h \
netbase.h \
@@ -454,6 +455,7 @@ libbitcoin_common_a_SOURCES = \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
+ net_permissions.cpp \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 3ae8498a87..6d8faf3883 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -250,8 +250,6 @@ BITCOIN_QT_H = \
RES_ICONS = \
qt/res/icons/add.png \
qt/res/icons/address-book.png \
- qt/res/icons/about.png \
- qt/res/icons/about_qt.png \
qt/res/icons/bitcoin.ico \
qt/res/icons/bitcoin_testnet.ico \
qt/res/icons/bitcoin.png \
@@ -261,13 +259,11 @@ RES_ICONS = \
qt/res/icons/clock3.png \
qt/res/icons/clock4.png \
qt/res/icons/clock5.png \
- qt/res/icons/configure.png \
qt/res/icons/connect0.png \
qt/res/icons/connect1.png \
qt/res/icons/connect2.png \
qt/res/icons/connect3.png \
qt/res/icons/connect4.png \
- qt/res/icons/debugwindow.png \
qt/res/icons/edit.png \
qt/res/icons/editcopy.png \
qt/res/icons/editpaste.png \
@@ -275,21 +271,16 @@ RES_ICONS = \
qt/res/icons/eye.png \
qt/res/icons/eye_minus.png \
qt/res/icons/eye_plus.png \
- qt/res/icons/filesave.png \
qt/res/icons/fontbigger.png \
qt/res/icons/fontsmaller.png \
qt/res/icons/hd_disabled.png \
qt/res/icons/hd_enabled.png \
qt/res/icons/history.png \
- qt/res/icons/info.png \
- qt/res/icons/key.png \
qt/res/icons/lock_closed.png \
qt/res/icons/lock_open.png \
qt/res/icons/network_disabled.png \
- qt/res/icons/open.png \
qt/res/icons/overview.png \
qt/res/icons/proxy.png \
- qt/res/icons/quit.png \
qt/res/icons/receive.png \
qt/res/icons/remove.png \
qt/res/icons/send.png \
@@ -302,8 +293,7 @@ RES_ICONS = \
qt/res/icons/tx_input.png \
qt/res/icons/tx_output.png \
qt/res/icons/tx_mined.png \
- qt/res/icons/warning.png \
- qt/res/icons/verify.png
+ qt/res/icons/warning.png
BITCOIN_QT_BASE_CPP = \
qt/bantablemodel.cpp \
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 5f6d69a4f3..cde624ce74 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -125,7 +125,7 @@ static int AppInitRPC(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 203f909cc4..361fedf35a 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -57,7 +57,7 @@ static bool WalletAppInit(int argc, char* argv[])
// check for printtoconsole, allow -debug
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 8e31f6e32b..cb3c4f70b4 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -95,8 +95,7 @@ static bool AppInit(int argc, char* argv[])
try
{
- if (!fs::is_directory(GetDataDir(false)))
- {
+ if (!CheckDataDirOption()) {
return InitError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
}
if (!gArgs.ReadConfigFiles(error, true)) {
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index c24234aeb7..ad766471dc 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -69,6 +69,8 @@ public:
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
+ consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5
+ consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -80,16 +82,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // Deployment of BIP68, BIP112, and BIP113.
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
-
- // Deployment of SegWit (BIP141, BIP143, and BIP147)
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1479168000; // November 15th, 2016.
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017.
-
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000051dc8b82f450202ecb3d471");
@@ -183,6 +175,8 @@ public:
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
+ consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb
+ consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -194,16 +188,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // Deployment of BIP68, BIP112, and BIP113.
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
-
- // Deployment of SegWit (BIP141, BIP143, and BIP147)
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1462060800; // May 1st 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1493596800; // May 1st 2017
-
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000007dbe94253893cbd463");
@@ -275,6 +259,8 @@ public:
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests)
consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests)
+ consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
+ consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -285,12 +271,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
@@ -307,7 +287,7 @@ public:
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
- UpdateVersionBitsParametersFromArgs(args);
+ UpdateActivationParametersFromArgs(args);
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
@@ -350,11 +330,22 @@ public:
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
}
- void UpdateVersionBitsParametersFromArgs(const ArgsManager& args);
+ void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
-void CRegTestParams::UpdateVersionBitsParametersFromArgs(const ArgsManager& args)
+void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
{
+ if (gArgs.IsArgSet("-segwitheight")) {
+ int64_t height = gArgs.GetArg("-segwitheight", consensus.SegwitHeight);
+ if (height < -1 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range. Use -1 to disable segwit.", height));
+ } else if (height == -1) {
+ LogPrintf("Segwit disabled for testing\n");
+ height = std::numeric_limits<int>::max();
+ }
+ consensus.SegwitHeight = static_cast<int>(height);
+ }
+
if (!args.IsArgSet("-vbparams")) return;
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index deb8e0fb57..9b98dff3ca 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -19,6 +19,7 @@ void SetupChainParamsBaseOptions()
{
gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-testnet", "Use the test chain", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
}
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6c3a201f4f..8263b0fef4 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -16,8 +16,6 @@ namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
- DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113.
- DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147.
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
@@ -58,6 +56,12 @@ struct Params {
int BIP65Height;
/** Block height at which BIP66 becomes active */
int BIP66Height;
+ /** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */
+ int CSVHeight;
+ /** Block height at which Segwit (BIP141, BIP143 and BIP147) becomes active.
+ * Note that segwit v0 script rules are enforced on all blocks except the
+ * BIP 16 exception blocks. */
+ int SegwitHeight;
/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
diff --git a/src/init.cpp b/src/init.cpp
index bb3ff8d88f..dd43ef71c9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -15,7 +15,6 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <coins.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
@@ -27,6 +26,7 @@
#include <key.h>
#include <miner.h>
#include <net.h>
+#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/feerate.h>
@@ -149,7 +149,6 @@ NODISCARD static bool CreatePidFile()
// shutdown thing.
//
-static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
static boost::thread_group threadGroup;
@@ -234,8 +233,14 @@ void Shutdown(InitInterfaces& interfaces)
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
- if (pcoinsTip != nullptr) {
- ::ChainstateActive().ForceFlushStateToDisk();
+ //
+ // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
+ // may not have been initialized yet.
+ {
+ LOCK(cs_main);
+ if (g_chainstate && g_chainstate->CanFlushToDisk()) {
+ g_chainstate->ForceFlushStateToDisk();
+ }
}
// After there are no more peers/RPC left to give us new data which may generate
@@ -250,12 +255,10 @@ void Shutdown(InitInterfaces& interfaces)
{
LOCK(cs_main);
- if (pcoinsTip != nullptr) {
- ::ChainstateActive().ForceFlushStateToDisk();
+ if (g_chainstate && g_chainstate->CanFlushToDisk()) {
+ g_chainstate->ForceFlushStateToDisk();
+ g_chainstate->ResetCoinsViews();
}
- pcoinsTip.reset();
- pcoinscatcher.reset();
- pcoinsdbview.reset();
pblocktree.reset();
}
for (const auto& client : interfaces.chain_clients) {
@@ -363,7 +366,7 @@ void SetupServerArgs()
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet, RPC and relay whitelisted inbound peers are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
@@ -507,8 +510,8 @@ void SetupServerArgs()
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted inbound peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted inbound peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
@@ -821,11 +824,6 @@ void InitParameterInteraction()
}
}
-static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
-{
- return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
-}
-
/**
* Initialize global loggers.
*
@@ -1466,10 +1464,10 @@ bool AppInitMain(InitInterfaces& interfaces)
bool is_coinsview_empty;
try {
LOCK(cs_main);
+ // This statement makes ::ChainstateActive() usable.
+ g_chainstate = MakeUnique<CChainState>();
UnloadBlockIndex();
- pcoinsTip.reset();
- pcoinsdbview.reset();
- pcoinscatcher.reset();
+
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
pblocktree.reset();
@@ -1520,9 +1518,12 @@ bool AppInitMain(InitInterfaces& interfaces)
// At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()!
- pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
- pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
- pcoinscatcher->AddReadErrCallback([]() {
+ ::ChainstateActive().InitCoinsDB(
+ /* cache_size_bytes */ nCoinDBCache,
+ /* in_memory */ false,
+ /* should_wipe */ fReset || fReindexChainState);
+
+ ::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down.").translated,
"", CClientUIInterface::MSG_ERROR);
@@ -1530,23 +1531,25 @@ bool AppInitMain(InitInterfaces& interfaces)
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!pcoinsdbview->Upgrade()) {
+ if (!::ChainstateActive().CoinsDB().Upgrade()) {
strLoadError = _("Error upgrading chainstate database").translated;
break;
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!ReplayBlocks(chainparams, pcoinsdbview.get())) {
+ if (!ReplayBlocks(chainparams, &::ChainstateActive().CoinsDB())) {
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
break;
}
// The on-disk coinsdb is now in a good state, create the cache
- pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
+ ::ChainstateActive().InitCoinsCache();
+ assert(::ChainstateActive().CanFlushToDisk());
- is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
+ is_coinsview_empty = fReset || fReindexChainState ||
+ ::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
if (!is_coinsview_empty) {
- // LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
+ // LoadChainTip sets ::ChainActive() based on CoinsTip()'s best block
if (!LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database").translated;
break;
@@ -1588,7 +1591,7 @@ bool AppInitMain(InitInterfaces& interfaces)
break;
}
- if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected").translated;
break;
@@ -1670,12 +1673,9 @@ bool AppInitMain(InitInterfaces& interfaces)
}
}
- if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
- // Only advertise witness capabilities if they have a reasonable start time.
- // This allows us to have the code merged without a defined softfork, by setting its
- // end time to 0.
- // Note that setting NODE_WITNESS is never required: the only downside from not
- // doing so is that after activation, no upgraded nodes will fetch from you.
+ if (chainparams.GetConsensus().SegwitHeight != std::numeric_limits<int>::max()) {
+ // 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);
}
@@ -1775,21 +1775,16 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
- CService addrBind;
- if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
- return InitError(ResolveErrMsg("whitebind", strBind));
- }
- if (addrBind.GetPort() == 0) {
- return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
- }
- connOptions.vWhiteBinds.push_back(addrBind);
+ NetWhitebindPermissions whitebind;
+ std::string error;
+ if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
+ connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
- CSubNet subnet;
- LookupSubNet(net.c_str(), subnet);
- if (!subnet.IsValid())
- return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
+ NetWhitelistPermissions subnet;
+ std::string error;
+ if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index bcd226edd9..fc49817502 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -232,7 +232,7 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- return ::pcoinsTip->GetCoin(output, coin);
+ return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
}
std::string getWalletDir() override
{
diff --git a/src/net.cpp b/src/net.cpp
index 7d6eb31a7c..337d1f6a46 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <crypto/common.h>
#include <crypto/sha256.h>
#include <netbase.h>
+#include <net_permissions.h>
#include <primitives/transaction.h>
#include <scheduler.h>
#include <ui_interface.h>
@@ -67,7 +68,6 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
- BF_WHITELIST = (1U << 2),
};
// The set of sockets cannot be modified while waiting
@@ -459,12 +459,10 @@ void CNode::CloseSocketDisconnect()
}
}
-bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
- for (const CSubNet& subnet : vWhitelistedRange) {
- if (subnet.Match(addr))
- return true;
+void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
+ for (const auto& subnet : vWhitelistedRange) {
+ if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
}
- return false;
}
std::string CNode::GetAddrName() const {
@@ -528,7 +526,8 @@ void CNode::copyStats(CNodeStats &stats)
X(mapRecvBytesPerMsgCmd);
X(nRecvBytes);
}
- X(fWhitelisted);
+ X(m_legacyWhitelisted);
+ X(m_permissionFlags);
{
LOCK(cs_feeFilter);
X(minFeeFilter);
@@ -813,7 +812,7 @@ bool CConnman::AttemptToEvictConnection()
LOCK(cs_vNodes);
for (const CNode* node : vNodes) {
- if (node->fWhitelisted)
+ if (node->HasPermission(PF_NOBAN))
continue;
if (!node->fInbound)
continue;
@@ -904,7 +903,19 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
+ NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
+ hListenSocket.AddSocketPermissionFlags(permissionFlags);
+ AddWhitelistPermissionFlags(permissionFlags, addr);
+ bool legacyWhitelisted = false;
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
+ NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
+ if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
+ if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
+ NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
+ NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
+ legacyWhitelisted = true;
+ }
+
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
@@ -941,7 +952,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
// if the only banning reason was an automatic misbehavior ban.
- if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -962,9 +973,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
+ ServiceFlags nodeServices = nLocalServices;
+ if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
+ nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
+ }
+ CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
- pnode->fWhitelisted = whitelisted;
+ pnode->m_permissionFlags = permissionFlags;
+ // If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
+ pnode->m_legacyWhitelisted = legacyWhitelisted;
pnode->m_prefer_evict = bannedlevel > 0;
m_msgproc->InitializeNode(pnode);
@@ -1983,7 +2000,7 @@ void CConnman::ThreadMessageHandler()
-bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
+bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions)
{
strError = "";
int nOne = 1;
@@ -2044,9 +2061,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return false;
}
- vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
+ vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
- if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
+ if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -2130,11 +2147,11 @@ NodeId CConnman::GetNewNodeId()
}
-bool CConnman::Bind(const CService &addr, unsigned int flags) {
+bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
+ if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR);
}
@@ -2143,20 +2160,21 @@ bool CConnman::Bind(const CService &addr, unsigned int flags) {
return true;
}
-bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) {
+bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
+{
bool fBound = false;
for (const auto& addrBind : binds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
}
for (const auto& addrBind : whiteBinds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
- fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
}
return fBound;
}
diff --git a/src/net.h b/src/net.h
index 37aaf1a63b..6c77d8135f 100644
--- a/src/net.h
+++ b/src/net.h
@@ -15,6 +15,7 @@
#include <hash.h>
#include <limitedmap.h>
#include <netaddress.h>
+#include <net_permissions.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -39,6 +40,11 @@ class CScheduler;
class CNode;
class BanMan;
+/** Default for -whitelistrelay. */
+static const bool DEFAULT_WHITELISTRELAY = true;
+/** Default for -whitelistforcerelay. */
+static const bool DEFAULT_WHITELISTFORCERELAY = false;
+
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
@@ -138,8 +144,9 @@ public:
uint64_t nMaxOutboundLimit = 0;
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
std::vector<std::string> vSeedNodes;
- std::vector<CSubNet> vWhitelistedRange;
- std::vector<CService> vBinds, vWhiteBinds;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
+ std::vector<NetWhitebindPermissions> vWhiteBinds;
+ std::vector<CService> vBinds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -314,15 +321,17 @@ public:
private:
struct ListenSocket {
+ public:
SOCKET socket;
- bool whitelisted;
-
- ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
+ inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
+ ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
+ private:
+ NetPermissionFlags m_permissions;
};
- bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
- bool Bind(const CService &addr, unsigned int flags);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
+ bool BindListenPort(const CService& bindAddr, std::string& 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);
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
@@ -347,7 +356,7 @@ private:
bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
- bool IsWhitelistedRange(const CNetAddr &addr);
+ void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void DeleteNode(CNode* pnode);
@@ -380,7 +389,7 @@ private:
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
- std::vector<CSubNet> vWhitelistedRange;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
@@ -448,7 +457,6 @@ void StartMapPort();
void InterruptMapPort();
void StopMapPort();
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
struct CombinerAll
{
@@ -555,7 +563,8 @@ public:
mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
- bool fWhitelisted;
+ NetPermissionFlags m_permissionFlags;
+ bool m_legacyWhitelisted;
double dPingTime;
double dPingWait;
double dMinPing;
@@ -657,7 +666,11 @@ public:
*/
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
- bool fWhitelisted{false}; // This peer can bypass DoS banning.
+ bool HasPermission(NetPermissionFlags permission) const {
+ return NetPermissions::HasFlag(m_permissionFlags, permission);
+ }
+ // This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
+ bool m_legacyWhitelisted{false};
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
bool fOneShot{false};
bool m_manual_connection{false};
@@ -753,6 +766,7 @@ private:
const ServiceFlags nLocalServices;
const int nMyStartingHeight;
int nSendVersion{0};
+ NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
new file mode 100644
index 0000000000..ef6c40ce20
--- /dev/null
+++ b/src/net_permissions.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2009-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.
+
+#include <net_permissions.h>
+#include <netbase.h>
+#include <util/error.h>
+#include <util/system.h>
+#include <util/translation.h>
+
+// The parse the following format "perm1,perm2@xxxxxx"
+bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, std::string& error)
+{
+ NetPermissionFlags flags = PF_NONE;
+ const auto atSeparator = str.find('@');
+
+ // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
+ if (atSeparator == std::string::npos) {
+ NetPermissions::AddFlag(flags, PF_ISIMPLICIT);
+ readen = 0;
+ }
+ // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
+ else {
+ readen = 0;
+ // permissions == perm1,perm2
+ const auto permissions = str.substr(0, atSeparator);
+ while (readen < permissions.length()) {
+ const auto commaSeparator = permissions.find(',', readen);
+ const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
+ // permission == perm1
+ const auto permission = permissions.substr(readen, len);
+ readen += len; // We read "perm1"
+ if (commaSeparator != std::string::npos) readen++; // We read ","
+
+ if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, PF_BLOOMFILTER);
+ else if (permission == "noban") NetPermissions::AddFlag(flags, PF_NOBAN);
+ else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
+ else if (permission == "mempool") NetPermissions::AddFlag(flags, PF_MEMPOOL);
+ else if (permission == "all") NetPermissions::AddFlag(flags, PF_ALL);
+ else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
+ else if (permission.length() == 0); // Allow empty entries
+ else {
+ error = strprintf(_("Invalid P2P permission: '%s'").translated, permission);
+ return false;
+ }
+ }
+ readen++;
+ }
+
+ output = flags;
+ error = "";
+ return true;
+}
+
+std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
+{
+ std::vector<std::string> strings;
+ if (NetPermissions::HasFlag(flags, PF_BLOOMFILTER)) strings.push_back("bloomfilter");
+ if (NetPermissions::HasFlag(flags, PF_NOBAN)) strings.push_back("noban");
+ if (NetPermissions::HasFlag(flags, PF_FORCERELAY)) strings.push_back("forcerelay");
+ if (NetPermissions::HasFlag(flags, PF_RELAY)) strings.push_back("relay");
+ if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) strings.push_back("mempool");
+ return strings;
+}
+
+bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string strBind = str.substr(offset);
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
+ error = ResolveErrMsg("whitebind", strBind);
+ return false;
+ }
+ if (addrBind.GetPort() == 0) {
+ error = strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_service = addrBind;
+ error = "";
+ return true;
+}
+
+bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string net = str.substr(offset);
+ CSubNet subnet;
+ LookupSubNet(net.c_str(), subnet);
+ if (!subnet.IsValid()) {
+ error = strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_subnet = subnet;
+ error = "";
+ return true;
+}
diff --git a/src/net_permissions.h b/src/net_permissions.h
new file mode 100644
index 0000000000..b3987de65f
--- /dev/null
+++ b/src/net_permissions.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2009-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.
+
+#include <string>
+#include <vector>
+#include <netaddress.h>
+
+#ifndef BITCOIN_NET_PERMISSIONS_H
+#define BITCOIN_NET_PERMISSIONS_H
+enum NetPermissionFlags
+{
+ PF_NONE = 0,
+ // Can query bloomfilter even if -peerbloomfilters is false
+ PF_BLOOMFILTER = (1U << 1),
+ // Relay and accept transactions from this peer, even if -blocksonly is true
+ PF_RELAY = (1U << 3),
+ // Always relay transactions from this peer, even if already in mempool or rejected from policy
+ // Keep parameter interaction: forcerelay implies relay
+ PF_FORCERELAY = (1U << 2) | PF_RELAY,
+ // Can't be banned for misbehavior
+ PF_NOBAN = (1U << 4),
+ // Can query the mempool
+ PF_MEMPOOL = (1U << 5),
+
+ // True if the user did not specifically set fine grained permissions
+ PF_ISIMPLICIT = (1U << 31),
+ PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL,
+};
+class NetPermissions
+{
+public:
+ NetPermissionFlags m_flags;
+ static std::vector<std::string> ToStrings(NetPermissionFlags flags);
+ static inline bool HasFlag(const NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ return (flags & f) == f;
+ }
+ static inline void AddFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags | f);
+ }
+ static inline void ClearFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags & ~f);
+ }
+};
+class NetWhitebindPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error);
+ CService m_service;
+};
+
+class NetWhitelistPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error);
+ CSubNet m_subnet;
+};
+
+#endif // BITCOIN_NET_PERMISSIONS_H \ No newline at end of file
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5efb4adee6..520dfcbb66 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -408,7 +408,7 @@ static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LO
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
+ state->fPreferredDownload = (!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && !node->fClient;
nPreferredDownload += state->fPreferredDownload;
}
@@ -1291,11 +1291,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
LOCK(g_cs_orphans);
if (mapOrphanTransactions.count(inv.hash)) return true;
}
+ const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
return recentRejects->contains(inv.hash) ||
mempool.exists(inv.hash) ||
- pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
- pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
+ coins_cache.HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
+ coins_cache.HaveCoinInCache(COutPoint(inv.hash, 1));
}
case MSG_BLOCK:
case MSG_WITNESS_BLOCK:
@@ -1398,7 +1399,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
// never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
{
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
@@ -1407,7 +1408,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->fWhitelisted && (
+ if (send && !pfrom->HasPermission(PF_NOBAN) && (
(((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
@@ -1844,7 +1845,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
EraseOrphanTx(orphanHash);
done = true;
}
- mempool.check(pcoinsTip.get());
+ mempool.check(&::ChainstateActive().CoinsTip());
}
}
@@ -2217,7 +2218,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool fBlocksOnly = !g_relay_txes;
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
+ if (pfrom->HasPermission(PF_RELAY))
fBlocksOnly = false;
LOCK(cs_main);
@@ -2412,7 +2413,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -2470,7 +2471,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (strCommand == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ if (!g_relay_txes && !pfrom->HasPermission(PF_RELAY))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
return true;
@@ -2497,7 +2498,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!AlreadyHave(inv) &&
AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
- mempool.check(pcoinsTip.get());
+ mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), *connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
@@ -2565,7 +2566,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ if (pfrom->HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
// if they were already in the mempool or rejected from it due
// to policy, allowing the node to function as a gateway for
@@ -3010,17 +3011,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
- if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ if (connman->OutboundTargetReached(false) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
@@ -3216,7 +3223,7 @@ bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_
if (state.fShouldBan) {
state.fShouldBan = false;
- if (pnode->fWhitelisted)
+ if (pnode->HasPermission(PF_NOBAN))
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
else if (pnode->m_manual_connection)
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
@@ -3786,7 +3793,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->vInventoryBlockToSend.clear();
// Check whether periodic sends should happen
- bool fSendTrickle = pto->fWhitelisted;
+ bool fSendTrickle = pto->HasPermission(PF_NOBAN);
if (pto->nNextInvSend < nNow) {
fSendTrickle = true;
if (pto->fInbound) {
@@ -3942,7 +3949,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Note: If all our peers are inbound, then we won't
// disconnect our sync peer for stalling; we have bigger
// problems if we can't get any outbound peers.
- if (!pto->fWhitelisted) {
+ if (!pto->HasPermission(PF_NOBAN)) {
LogPrintf("Timeout downloading headers from peer=%d, disconnecting\n", pto->GetId());
pto->fDisconnect = true;
return true;
@@ -4060,7 +4067,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !(pto->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
+ !pto->HasPermission(PF_FORCERELAY)) {
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
if (timeNow > pto->nextSendTimeFeeFilter) {
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index bb98e63f3a..ad8d1d3af4 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -10,8 +10,7 @@
void FindCoins(std::map<COutPoint, Coin>& coins)
{
LOCK2(cs_main, ::mempool.cs);
- assert(pcoinsTip);
- CCoinsViewCache& chain_view = *::pcoinsTip;
+ CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
for (auto& coin : coins) {
if (!mempool_view.GetCoin(coin.first, coin.second)) {
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index a28136a8e8..7e8291ddc8 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -28,7 +28,7 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, std::string& err
LOCK(cs_main);
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
- CCoinsViewCache &view = *pcoinsTip;
+ CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
// IsSpent doesnt mean the coin is spent, it means the output doesnt' exist.
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 482bf0543d..adc19df935 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -169,8 +169,11 @@ void BitcoinCore::shutdown()
}
}
-BitcoinApplication::BitcoinApplication(interfaces::Node& node, int &argc, char **argv):
- QApplication(argc, argv),
+static int qt_argc = 1;
+static const char* qt_argv = "bitcoin-qt";
+
+BitcoinApplication::BitcoinApplication(interfaces::Node& node):
+ QApplication(qt_argc, const_cast<char **>(&qt_argv)),
coreThread(nullptr),
m_node(node),
optionsModel(nullptr),
@@ -433,7 +436,7 @@ int GuiMain(int argc, char* argv[])
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
- BitcoinApplication app(*node, argc, argv);
+ BitcoinApplication app(*node);
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
@@ -487,10 +490,9 @@ int GuiMain(int argc, char* argv[])
if (!Intro::pickDataDirectory(*node))
return EXIT_SUCCESS;
- /// 6. Determine availability of data and blocks directory and parse bitcoin.conf
+ /// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
- if (!fs::is_directory(GetDataDir(false)))
- {
+ if (!CheckDataDirOption()) {
node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 40537c1813..3869193a3a 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -56,7 +56,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(interfaces::Node& node, int &argc, char **argv);
+ explicit BitcoinApplication(interfaces::Node& node);
~BitcoinApplication();
#ifdef ENABLE_WALLET
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index fddc2a5685..037b23e4b2 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -2,7 +2,6 @@
<qresource prefix="/icons">
<file alias="bitcoin">res/icons/bitcoin.png</file>
<file alias="address-book">res/icons/address-book.png</file>
- <file alias="quit">res/icons/quit.png</file>
<file alias="send">res/icons/send.png</file>
<file alias="connect_0">res/icons/connect0.png</file>
<file alias="connect_1">res/icons/connect1.png</file>
@@ -20,7 +19,6 @@
<file alias="eye">res/icons/eye.png</file>
<file alias="eye_minus">res/icons/eye_minus.png</file>
<file alias="eye_plus">res/icons/eye_plus.png</file>
- <file alias="options">res/icons/configure.png</file>
<file alias="receiving_addresses">res/icons/receive.png</file>
<file alias="editpaste">res/icons/editpaste.png</file>
<file alias="editcopy">res/icons/editcopy.png</file>
@@ -37,14 +35,6 @@
<file alias="tx_inout">res/icons/tx_inout.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
- <file alias="key">res/icons/key.png</file>
- <file alias="filesave">res/icons/filesave.png</file>
- <file alias="debugwindow">res/icons/debugwindow.png</file>
- <file alias="open">res/icons/open.png</file>
- <file alias="info">res/icons/info.png</file>
- <file alias="about">res/icons/about.png</file>
- <file alias="about_qt">res/icons/about_qt.png</file>
- <file alias="verify">res/icons/verify.png</file>
<file alias="warning">res/icons/warning.png</file>
<file alias="fontbigger">res/icons/fontbigger.png</file>
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 3533227483..bc9af9793e 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -248,7 +248,7 @@ void BitcoinGUI::createActions()
sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
tabGroup->addAction(sendCoinsAction);
- sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this);
+ sendCoinsMenuAction = new QAction(sendCoinsAction->text(), this);
sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip());
sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip());
@@ -259,7 +259,7 @@ void BitcoinGUI::createActions()
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
tabGroup->addAction(receiveCoinsAction);
- receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this);
+ receiveCoinsMenuAction = new QAction(receiveCoinsAction->text(), this);
receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip());
receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip());
@@ -287,48 +287,48 @@ void BitcoinGUI::createActions()
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
#endif // ENABLE_WALLET
- quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this);
+ quitAction = new QAction(tr("E&xit"), this);
quitAction->setStatusTip(tr("Quit application"));
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
quitAction->setMenuRole(QAction::QuitRole);
- aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(PACKAGE_NAME), this);
+ aboutAction = new QAction(tr("&About %1").arg(PACKAGE_NAME), this);
aboutAction->setStatusTip(tr("Show information about %1").arg(PACKAGE_NAME));
aboutAction->setMenuRole(QAction::AboutRole);
aboutAction->setEnabled(false);
- aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this);
+ aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
- optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this);
+ optionsAction = new QAction(tr("&Options..."), this);
optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(PACKAGE_NAME));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
- toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this);
+ toggleHideAction = new QAction(tr("&Show / Hide"), this);
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
- encryptWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
+ encryptWalletAction = new QAction(tr("&Encrypt Wallet..."), this);
encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
- backupWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
+ backupWalletAction = new QAction(tr("&Backup Wallet..."), this);
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
- changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this);
+ changePassphraseAction = new QAction(tr("&Change Passphrase..."), this);
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
- signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this);
+ signMessageAction = new QAction(tr("Sign &message..."), this);
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
- verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this);
+ verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this);
+ openRPCConsoleAction = new QAction(tr("&Debug window"), this);
openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
// initially disable the debug window menu item
openRPCConsoleAction->setEnabled(false);
openRPCConsoleAction->setObjectName("openRPCConsoleAction");
- usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses"), this);
+ usedSendingAddressesAction = new QAction(tr("&Sending addresses"), this);
usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels"));
- usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses"), this);
+ usedReceivingAddressesAction = new QAction(tr("&Receiving addresses"), this);
usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
- openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this);
+ openAction = new QAction(tr("Open &URI..."), this);
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
m_open_wallet_action = new QAction(tr("Open Wallet"), this);
@@ -339,7 +339,7 @@ void BitcoinGUI::createActions()
m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
m_close_wallet_action->setStatusTip(tr("Close wallet"));
- showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this);
+ showHelpMessageAction = new QAction(tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
diff --git a/src/qt/res/icons/about.png b/src/qt/res/icons/about.png
deleted file mode 100644
index 4143be8bac..0000000000
--- a/src/qt/res/icons/about.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/about_qt.png b/src/qt/res/icons/about_qt.png
deleted file mode 100644
index c40abfd3a6..0000000000
--- a/src/qt/res/icons/about_qt.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/configure.png b/src/qt/res/icons/configure.png
deleted file mode 100644
index 5333c83d5e..0000000000
--- a/src/qt/res/icons/configure.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/debugwindow.png b/src/qt/res/icons/debugwindow.png
deleted file mode 100644
index 290fe60864..0000000000
--- a/src/qt/res/icons/debugwindow.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/filesave.png b/src/qt/res/icons/filesave.png
deleted file mode 100644
index 779cca1d52..0000000000
--- a/src/qt/res/icons/filesave.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/info.png b/src/qt/res/icons/info.png
deleted file mode 100644
index 692b50c2a9..0000000000
--- a/src/qt/res/icons/info.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png
deleted file mode 100644
index f301c4f38c..0000000000
--- a/src/qt/res/icons/key.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/open.png b/src/qt/res/icons/open.png
deleted file mode 100644
index 4d958f0e18..0000000000
--- a/src/qt/res/icons/open.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png
deleted file mode 100644
index 55e34de4b8..0000000000
--- a/src/qt/res/icons/quit.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/verify.png b/src/qt/res/icons/verify.png
deleted file mode 100644
index 8e2cb2cc14..0000000000
--- a/src/qt/res/icons/verify.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/src/verify.svg b/src/qt/res/src/verify.svg
deleted file mode 100644
index 1ff11b7f5e..0000000000
--- a/src/qt/res/src/verify.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 841.9 595.3" enable-background="new 0 0 841.9 595.3" xml:space="preserve">
-<path d="M654.1,317.5c-14.9-9.9-37.2-2.5-44.6,12.4l-62,111.6l-34.7-34.7c-12.4-12.4-34.7-12.4-47.1,0c-12.4,12.4-12.4,34.7,0,47.1
- l67,67c7.4,7.4,14.9,9.9,22.3,9.9h5c9.9-2.5,19.8-7.4,24.8-17.4l81.9-148.8C676.4,347.2,671.5,327.4,654.1,317.5z"/>
-<path d="M326.7,471.3H177.9V362.1l94.3-94.3c-5-14.9-7.4-29.8-7.4-44.6c0-81.9,67-148.8,148.8-148.8s148.8,67,148.8,148.8
- s-67,148.8-148.8,148.8h-37.2v49.6h-49.6L326.7,471.3L326.7,471.3z M227.5,421.7h49.6v-49.6h49.6v-49.6h86.8
- c54.6,0,99.2-44.6,99.2-99.2S468.1,124,413.5,124s-99.2,44.6-99.2,99.2c0,14.9,2.5,27.3,9.9,39.7l7.4,14.9L230,379.5v42.2H227.5z
- M413.5,198.4c14.9,0,24.8,9.9,24.8,24.8c0,14.9-9.9,24.8-24.8,24.8c-14.9,0-24.8-9.9-24.8-24.8
- C388.7,208.3,401.1,198.4,413.5,198.4 M413.5,173.6c-27.3,0-49.6,22.3-49.6,49.6c0,27.3,22.3,49.6,49.6,49.6
- c27.3,0,49.6-22.3,49.6-49.6C463.1,195.9,443.3,173.6,413.5,173.6z"/>
-</svg>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index cdf84eae9a..19b11ba1cd 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1120,7 +1120,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
- ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes") : tr("No"));
+ ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index dd5216d68c..796cf24b36 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -68,7 +68,7 @@ int main(int argc, char *argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
- BitcoinApplication app(*node, argc, argv);
+ BitcoinApplication app(*node);
app.setApplicationName("Bitcoin-Qt-test");
AppTests app_tests(app);
diff --git a/src/rest.cpp b/src/rest.cpp
index eba7aae50f..2c4d475542 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -503,12 +503,12 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
if (fCheckMemPool) {
// use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool.cs);
- CCoinsViewCache& viewChain = *pcoinsTip;
+ CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
process_utxos(viewMempool, mempool);
} else {
LOCK(cs_main); // no need to lock mempool!
- process_utxos(*pcoinsTip, CTxMemPool());
+ process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
}
for (size_t i = 0; i < hits.size(); ++i) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index b7dcd59c6d..bfb6ab6a21 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1062,7 +1062,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
- if (GetUTXOStats(pcoinsdbview.get(), stats)) {
+
+ CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
+ if (GetUTXOStats(coins_view, stats)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1126,19 +1128,21 @@ UniValue gettxout(const JSONRPCRequest& request)
fMempool = request.params[2].get_bool();
Coin coin;
+ CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
+
if (fMempool) {
LOCK(mempool.cs);
- CCoinsViewMemPool view(pcoinsTip.get(), mempool);
+ CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return NullUniValue;
}
} else {
- if (!pcoinsTip->GetCoin(out, coin)) {
+ if (!coins_view->GetCoin(out, coin)) {
return NullUniValue;
}
}
- const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
+ const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0);
@@ -1180,57 +1184,53 @@ static UniValue verifychain(const JSONRPCRequest& request)
if (!request.params[1].isNull())
nCheckDepth = request.params[1].get_int();
- return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
+ return CVerifyDB().VerifyDB(
+ Params(), &::ChainstateActive().CoinsTip(), nCheckLevel, nCheckDepth);
}
-/** Implementation of IsSuperMajority with better feedback */
-static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
+static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- UniValue rv(UniValue::VOBJ);
- bool activated = false;
- switch(version)
- {
- case 2:
- activated = pindex->nHeight >= consensusParams.BIP34Height;
- break;
- case 3:
- activated = pindex->nHeight >= consensusParams.BIP66Height;
- break;
- case 4:
- activated = pindex->nHeight >= consensusParams.BIP65Height;
- break;
- }
- rv.pushKV("status", activated);
- return rv;
-}
+ // 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 (height == std::numeric_limits<int>::max()) return;
-static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
-{
UniValue rv(UniValue::VOBJ);
- rv.pushKV("id", name);
- rv.pushKV("version", version);
- rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams));
- return rv;
+ rv.pushKV("type", "buried");
+ // getblockchaininfo reports the softfork as active from when the chain height is
+ // one below the activation height
+ rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height);
+ rv.pushKV("height", height);
+ softforks.pushKV(name, rv);
}
-static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- UniValue rv(UniValue::VOBJ);
+ // For BIP9 deployments.
+ // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
+ // A timeout value of 0 guarantees a softfork will never be activated.
+ // This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
+ if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
+
+ UniValue bip9(UniValue::VOBJ);
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
switch (thresholdState) {
- case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
- case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
- case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break;
- case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break;
- case ThresholdState::FAILED: rv.pushKV("status", "failed"); break;
+ case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
+ case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
+ case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
+ case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
+ case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
}
if (ThresholdState::STARTED == thresholdState)
{
- rv.pushKV("bit", consensusParams.vDeployments[id].bit);
+ bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
- rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
- rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
+ bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
+ bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
+ int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
+ bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
@@ -1240,18 +1240,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
statsUV.pushKV("possible", statsStruct.possible);
- rv.pushKV("statistics", statsUV);
+ bip9.pushKV("statistics", statsUV);
}
- return rv;
-}
-static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
-{
- // Deployments with timeout value of 0 are hidden.
- // A timeout value of 0 guarantees a softfork will never be activated.
- // This is used when softfork codes are merged without specifying the deployment schedule.
- if (consensusParams.vDeployments[id].nTimeout > 0)
- bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id));
+ UniValue rv(UniValue::VOBJ);
+ rv.pushKV("type", "bip9");
+ rv.pushKV("bip9", bip9);
+ if (ThresholdState::ACTIVE == thresholdState) {
+ rv.pushKV("height", since_height);
+ }
+ rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
+
+ softforks.pushKV(name, rv);
}
UniValue getblockchaininfo(const JSONRPCRequest& request)
@@ -1275,29 +1275,25 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
- " \"softforks\": [ (array) status of softforks in progress\n"
- " {\n"
- " \"id\": \"xxxx\", (string) name of softfork\n"
- " \"version\": xx, (numeric) block version\n"
- " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
- " \"status\": xx, (boolean) true if threshold reached\n"
- " },\n"
- " }, ...\n"
- " ],\n"
- " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
+ " \"softforks\": { (object) status of softforks\n"
" \"xxxx\" : { (string) name of the softfork\n"
- " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
- " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
- " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
- " \"since\": xx, (numeric) height of the first block to which the status applies\n"
- " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
- " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
- " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
- " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
- " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
- " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
- " }\n"
+ " \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n"
+ " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n"
+ " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
+ " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
+ " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx, (numeric) height of the first block to which the status applies\n"
+ " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n"
+ " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
+ " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
+ " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
+ " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
+ " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
+ " }\n"
+ " },\n"
+ " \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n"
+ " \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n"
" }\n"
" }\n"
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
@@ -1342,16 +1338,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
}
const Consensus::Params& consensusParams = Params().GetConsensus();
- UniValue softforks(UniValue::VARR);
- UniValue bip9_softforks(UniValue::VOBJ);
- softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
- softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
- softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
- for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
- BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
- }
+ UniValue softforks(UniValue::VOBJ);
+ BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height);
+ BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height);
+ BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height);
+ BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight);
+ BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
+ BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
obj.pushKV("softforks", softforks);
- obj.pushKV("bip9_softforks", bip9_softforks);
obj.pushKV("warnings", GetWarnings("statusbar"));
return obj;
@@ -2203,7 +2197,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{
LOCK(cs_main);
::ChainstateActive().ForceFlushStateToDisk();
- pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
+ pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
assert(pcursor);
}
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 92a0e33769..07c2958635 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -482,9 +482,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
- const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
// GBT must be called with 'segwit' set in the rules
- if (setClientRules.count(segwit_info.name) != 1) {
+ if (setClientRules.count("segwit") != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
}
@@ -521,7 +520,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
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 = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
+ const bool fPreSegWit = (pindexPrev->nHeight + 1 < consensusParams.SegwitHeight);
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 6be4057366..1516007201 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -136,6 +136,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCResult{
"{\n"
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
+ " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
" \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
" \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
" \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
@@ -149,13 +150,15 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VSTR});
FlatSigningProvider provider;
- auto desc = Parse(request.params[0].get_str(), provider);
+ std::string error;
+ auto desc = Parse(request.params[0].get_str(), provider, error);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
UniValue result(UniValue::VOBJ);
result.pushKV("descriptor", desc->ToString());
+ result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
result.pushKV("isrange", desc->IsRange());
result.pushKV("issolvable", desc->IsSolvable());
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
@@ -197,9 +200,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
}
FlatSigningProvider key_provider;
- auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true);
+ std::string error;
+ auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
if (!desc->IsRange() && request.params.size() > 1) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 16b59e3d58..25dda924a4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -9,6 +9,7 @@
#include <core_io.h>
#include <net.h>
#include <net_processing.h>
+#include <net_permissions.h>
#include <netbase.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -177,7 +178,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}
obj.pushKV("inflight", heights);
}
- obj.pushKV("whitelisted", stats.fWhitelisted);
+ obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
+ UniValue permissions(UniValue::VARR);
+ for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
+ permissions.push_back(permission);
+ }
+ obj.pushKV("permissions", permissions);
obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter));
UniValue sendPerMsgCmd(UniValue::VOBJ);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 966c159f0f..ffbad45714 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -259,7 +259,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) {
- const Coin& coin = AccessByTxid(*pcoinsTip, tx);
+ const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx);
if (!coin.IsSpent()) {
pblockindex = ::ChainActive()[coin.nHeight];
break;
@@ -636,7 +636,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
LOCK(cs_main);
LOCK(mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@@ -1505,7 +1505,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CCoinsViewCache view(&viewDummy);
{
LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index de90276677..464537a5b0 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -717,9 +717,10 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
}
- auto desc = Parse(desc_str, provider);
+ std::string error;
+ auto desc = Parse(desc_str, provider, error);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
if (!desc->IsRange()) {
range.first = 0;
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 327af62a4f..b782ebbd1f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -690,7 +690,7 @@ std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
}
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
-NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
+NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error)
{
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
@@ -700,33 +700,60 @@ NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath&
hardened = true;
}
uint32_t p;
- if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false;
+ if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) {
+ error = strprintf("Key path value '%s' is not a valid uint32", std::string(elem.begin(), elem.end()).c_str());
+ return false;
+ } else if (p > 0x7FFFFFFFUL) {
+ error = strprintf("Key path value %u is out of range", p);
+ return false;
+ }
out.push_back(p | (((uint32_t)hardened) << 31));
}
return true;
}
/** Parse a public key that excludes origin information. */
-std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
+ if (str.size() == 0) {
+ error = "No key provided";
+ return nullptr;
+ }
if (split.size() == 1) {
if (IsHex(str)) {
std::vector<unsigned char> data = ParseHex(str);
CPubKey pubkey(data);
- if (pubkey.IsFullyValid() && (permit_uncompressed || pubkey.IsCompressed())) return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (pubkey.IsFullyValid()) {
+ if (permit_uncompressed || pubkey.IsCompressed()) {
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
+ }
+ error = strprintf("Pubkey '%s' is invalid", str);
+ return nullptr;
}
CKey key = DecodeSecret(str);
- if (key.IsValid() && (permit_uncompressed || key.IsCompressed())) {
- CPubKey pubkey = key.GetPubKey();
- out.keys.emplace(pubkey.GetID(), key);
- return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (key.IsValid()) {
+ if (permit_uncompressed || key.IsCompressed()) {
+ CPubKey pubkey = key.GetPubKey();
+ out.keys.emplace(pubkey.GetID(), key);
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
}
}
CExtKey extkey = DecodeExtKey(str);
CExtPubKey extpubkey = DecodeExtPubKey(str);
- if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr;
+ if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) {
+ error = strprintf("key '%s' is not valid", str);
+ return nullptr;
+ }
KeyPath path;
DeriveType type = DeriveType::NO;
if (split.back() == MakeSpan("*").first(1)) {
@@ -736,7 +763,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
split.pop_back();
type = DeriveType::HARDENED;
}
- if (!ParseKeyPath(split, path)) return nullptr;
+ if (!ParseKeyPath(split, path, error)) return nullptr;
if (extkey.key.IsValid()) {
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
@@ -745,95 +772,154 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
}
/** Parse a public key including origin information (if enabled). */
-std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto origin_split = Split(sp, ']');
- if (origin_split.size() > 2) return nullptr;
- if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out);
- if (origin_split[0].size() < 1 || origin_split[0][0] != '[') return nullptr;
+ if (origin_split.size() > 2) {
+ error = "Multiple ']' characters found for a single pubkey";
+ return nullptr;
+ }
+ if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out, error);
+ if (origin_split[0].size() < 1 || origin_split[0][0] != '[') {
+ error = strprintf("Key origin start '[ character expected but not found, got '%c' instead", origin_split[0][0]);
+ return nullptr;
+ }
auto slash_split = Split(origin_split[0].subspan(1), '/');
- if (slash_split[0].size() != 8) return nullptr;
+ if (slash_split[0].size() != 8) {
+ error = strprintf("Fingerprint is not 4 bytes (%u characters instead of 8 characters)", slash_split[0].size());
+ return nullptr;
+ }
std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
- if (!IsHex(fpr_hex)) return nullptr;
+ if (!IsHex(fpr_hex)) {
+ error = strprintf("Fingerprint '%s' is not hex", fpr_hex);
+ return nullptr;
+ }
auto fpr_bytes = ParseHex(fpr_hex);
KeyOriginInfo info;
static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
assert(fpr_bytes.size() == 4);
std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
- if (!ParseKeyPath(slash_split, info.path)) return nullptr;
- auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out);
+ if (!ParseKeyPath(slash_split, info.path, error)) return nullptr;
+ auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out, error);
if (!provider) return nullptr;
return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(provider));
}
/** Parse a script in a particular context. */
-std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
+std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
auto expr = Expr(sp);
if (Func("pk", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
- auto pubkey = ParsePubkey(expr, true, out);
+ auto pubkey = ParsePubkey(expr, true, out, error);
if (!pubkey) return nullptr;
return MakeUnique<ComboDescriptor>(std::move(pubkey));
+ } else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) {
+ error = "Cannot have combo in non-top level";
+ return nullptr;
}
if (Func("multi", expr)) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
- if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) return nullptr;
+ if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) {
+ error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()).c_str());
+ return nullptr;
+ }
size_t script_size = 0;
while (expr.size()) {
- if (!Const(",", expr)) return nullptr;
+ if (!Const(",", expr)) {
+ error = strprintf("Multi: expected ',', got '%c'", expr[0]);
+ return nullptr;
+ }
auto arg = Expr(expr);
- auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out);
+ auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out, error);
if (!pk) return nullptr;
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
}
- if (providers.size() < 1 || providers.size() > 16 || thres < 1 || thres > providers.size()) return nullptr;
+ if (providers.size() < 1 || providers.size() > 16) {
+ error = strprintf("Cannot have %u keys in multisig; must have between 1 and 16 keys, inclusive", providers.size());
+ return nullptr;
+ } else if (thres < 1) {
+ error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
+ return nullptr;
+ } else if (thres > providers.size()) {
+ error = strprintf("Multisig threshold cannot be larger than the number of keys; threshold is %d but only %u keys specified", thres, providers.size());
+ return nullptr;
+ }
if (ctx == ParseScriptContext::TOP) {
- if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig
+ if (providers.size() > 3) {
+ error = strprintf("Cannot have %u pubkeys in bare multisig; only at most 3 pubkeys", providers.size());
+ return nullptr;
+ }
}
if (ctx == ParseScriptContext::P2SH) {
- if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit
+ if (script_size + 3 > 520) {
+ error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3);
+ return nullptr;
+ }
}
return MakeUnique<MultisigDescriptor>(thres, std::move(providers));
}
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
- auto pubkey = ParsePubkey(expr, false, out);
+ auto pubkey = ParsePubkey(expr, false, out, error);
if (!pubkey) return nullptr;
return MakeUnique<WPKHDescriptor>(std::move(pubkey));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) {
+ error = "Cannot have wpkh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2SH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<SHDescriptor>(std::move(desc));
+ } else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) {
+ error = "Cannot have sh in non-top level";
+ return nullptr;
}
if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<WSHDescriptor>(std::move(desc));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) {
+ error = "Cannot have wsh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
- if (!IsValidDestination(dest)) return nullptr;
+ if (!IsValidDestination(dest)) {
+ error = "Address is not valid";
+ return nullptr;
+ }
return MakeUnique<AddressDescriptor>(std::move(dest));
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
- if (!IsHex(str)) return nullptr;
+ if (!IsHex(str)) {
+ error = "Raw script is not hex";
+ return nullptr;
+ }
auto bytes = ParseHex(str);
return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
}
+ if (ctx == ParseScriptContext::P2SH) {
+ error = "A function is needed within P2SH";
+ return nullptr;
+ } else if (ctx == ParseScriptContext::P2WSH) {
+ error = "A function is needed within P2WSH";
+ return nullptr;
+ }
+ error = strprintf("%s is not a valid descriptor function", std::string(expr.begin(), expr.end()));
return nullptr;
}
@@ -914,27 +1000,58 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
} // namespace
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
+/** Check a descriptor checksum, and update desc to be the checksum-less part. */
+bool CheckChecksum(Span<const char>& sp, bool require_checksum, std::string& error, std::string* out_checksum = nullptr)
{
- Span<const char> sp(descriptor.data(), descriptor.size());
-
- // Checksum checks
auto check_split = Split(sp, '#');
- if (check_split.size() > 2) return nullptr; // Multiple '#' symbols
- if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum
+ if (check_split.size() > 2) {
+ error = "Multiple '#' symbols";
+ return false;
+ }
+ if (check_split.size() == 1 && require_checksum){
+ error = "Missing checksum";
+ return false;
+ }
+ if (check_split.size() == 2) {
+ if (check_split[1].size() != 8) {
+ error = strprintf("Expected 8 character checksum, not %u characters", check_split[1].size());
+ return false;
+ }
+ }
+ auto checksum = DescriptorChecksum(check_split[0]);
+ if (checksum.empty()) {
+ error = "Invalid characters in payload";
+ return false;
+ }
if (check_split.size() == 2) {
- if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum
- auto checksum = DescriptorChecksum(check_split[0]);
- if (checksum.empty()) return nullptr; // Invalid characters in payload
- if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch
+ if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) {
+ error = strprintf("Provided checksum '%s' does not match computed checksum '%s'", std::string(check_split[1].begin(), check_split[1].end()), checksum);
+ return false;
+ }
}
+ if (out_checksum) *out_checksum = std::move(checksum);
sp = check_split[0];
+ return true;
+}
- auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)
+{
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
+ auto ret = ParseScript(sp, ParseScriptContext::TOP, out, error);
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
}
+std::string GetDescriptorChecksum(const std::string& descriptor)
+{
+ std::string ret;
+ std::string error;
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, false, error, &ret)) return "";
+ return ret;
+}
+
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
{
return InferScript(script, ParseScriptContext::TOP, provider);
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index a34e9f0d8a..0195ca0939 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -79,7 +79,15 @@ struct Descriptor {
* If a parse error occurs, or the checksum is missing/invalid, or anything
* else is wrong, nullptr is returned.
*/
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false);
+
+/** Get the checksum for a descriptor.
+ *
+ * If it already has one, and it is correct, return the checksum in the input.
+ * If it already has one that is wrong, return "".
+ * If it does not already have one, return the checksum that would need to be added.
+ */
+std::string GetDescriptorChecksum(const std::string& descriptor);
/** Find a descriptor for the specified script, using information from provider where possible.
*
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index f5bda7d5e6..1b9ec76385 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -13,13 +13,15 @@
namespace {
-void CheckUnparsable(const std::string& prv, const std::string& pub)
+void CheckUnparsable(const std::string& prv, const std::string& pub, const std::string& expected_error)
{
FlatSigningProvider keys_priv, keys_pub;
- auto parse_priv = Parse(prv, keys_priv);
- auto parse_pub = Parse(pub, keys_pub);
+ std::string error;
+ auto parse_priv = Parse(prv, keys_priv, error);
+ auto parse_pub = Parse(pub, keys_pub, error);
BOOST_CHECK_MESSAGE(!parse_priv, prv);
BOOST_CHECK_MESSAGE(!parse_pub, pub);
+ BOOST_CHECK(error == expected_error);
}
constexpr int DEFAULT = 0;
@@ -62,10 +64,11 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
{
FlatSigningProvider keys_priv, keys_pub;
std::set<std::vector<uint32_t>> left_paths = paths;
+ std::string error;
// Check that parsing succeeds.
- auto parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
- auto parse_pub = Parse(MaybeUseHInsteadOfApostrophy(pub), keys_pub);
+ auto parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv, error);
+ auto parse_pub = Parse(MaybeUseHInsteadOfApostrophy(pub), keys_pub, error);
BOOST_CHECK(parse_priv);
BOOST_CHECK(parse_pub);
@@ -176,14 +179,17 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, {{1,0x80000002UL,3,0x80000004UL}});
Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}});
Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
+ CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey
+ CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin
+ CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin
// Basic single-key uncompressed
Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}});
Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
- CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)"); // No uncompressed keys in witness
- CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))"); // No uncompressed keys in witness
- CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))"); // No uncompressed keys in witness
+ CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
+ CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
+ CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
// Some unconventional single-key constructions
Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}});
@@ -200,38 +206,50 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, {{0}, {1}});
- CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)"); // Too long key fingerprint
- CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)"); // BIP 32 path element overflow
+ CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
+ CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "Key path value 2147483648 is out of range"); // BIP 32 path element overflow
+ CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "Key path value '1aa' is not a valid uint32"); // Path is not valid uint
// Multisig constructions
Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}});
- CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
- CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Double key origin descriptor
- CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Non hex fingerprint
- CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // No public key with origin
- CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too short fingerprint
- CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too long fingerprint
+ CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
+ CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "No key provided"); // No public key with origin
+ CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint
+ CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold
+ CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
+ CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
+ CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
+ CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have 17 keys in multisig; must have between 1 and 16 keys, inclusive"); // Cannot have more than 16 keys in a multisig
// Check for invalid nesting of structures
- CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2SH needs a script, not a key
- CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))"); // Old must be top level
- CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2WSH needs a script, not a key
- CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))"); // Cannot embed witness inside witness
- CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2WSH
- CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2SH
- CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2WSH inside P2WSH
+ CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key
+ CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have combo in non-top level"); // Old must be top level
+ CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2WSH"); // P2WSH needs a script, not a key
+ CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have wpkh within wsh"); // Cannot embed witness inside witness
+ CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2WSH
+ CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2SH
+ CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have wsh within wsh"); // Cannot embed P2WSH inside P2WSH
// Checksums
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#"); // Empty checksum
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq"); // Too long checksum
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5"); // Too short checksum
- CheckUnparsable("sh(multi(3,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(3,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t"); // Error in payload
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjq09x4t"); // Error in checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum
+ CheckUnparsable("sh(multi(3,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(3,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "Provided checksum 'tjg09x5t' does not match computed checksum 'd4x0uxyv'"); // Error in payload
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjq09x4t", "Provided checksum 'tjq09x4t' does not match computed checksum 'tjg09x5t'"); // Error in checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))##ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))##tjq09x4t", "Multiple '#' symbols"); // Error in checksum
+
+ // Addr and raw tests
+ CheckUnparsable("", "addr(asdf)", "Address is not valid"); // Invalid address
+ CheckUnparsable("", "raw(asdf)", "Raw script is not hex"); // Invalid script
+ CheckUnparsable("", "raw(Ü)#00000000", "Invalid characters in payload"); // Invalid chars
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 05d7f76983..c9661b730d 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- pcoinsTip->SetBestBlock(next->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- pcoinsTip->SetBestBlock(next->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
while (::ChainActive().Tip()->nHeight > nHeight) {
CBlockIndex* del = ::ChainActive().Tip();
::ChainActive().SetTip(del->pprev);
- pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 86c0cecbf1..a3d0831624 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netbase.h>
+#include <net_permissions.h>
#include <test/setup_common.h>
#include <util/strencodings.h>
@@ -321,4 +322,82 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_CHECK_EQUAL(ParseNetwork(""), NET_UNROUTABLE);
}
+BOOST_AUTO_TEST_CASE(netpermissions_test)
+{
+ std::string error;
+ NetWhitebindPermissions whitebindPermissions;
+ NetWhitelistPermissions whitelistPermissions;
+
+ // Detect invalid white bind
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Cannot resolve -whitebind address") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("127.0.0.1", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Need to specify a port with -whitebind") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+
+ // If no permission flags, assume backward compatibility
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ NetPermissions::ClearFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(!NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ NetPermissions::AddFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+
+ // Can set one permission
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Happy path, can parse flags
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error));
+ // forcerelay should also activate the relay permission
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("all@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ALL);
+
+ // Allow dups
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+
+ // Allow empty
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Detect invalid flag
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Invalid P2P permission") != std::string::npos);
+
+ // Check whitelist error
+ BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
+ BOOST_CHECK(error.find("Invalid netmask specified in -whitelist") != std::string::npos);
+
+ // Happy path for whitelist parsing
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN);
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY);
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
+
+ const auto strings = NetPermissions::ToStrings(PF_ALL);
+ BOOST_CHECK_EQUAL(strings.size(), 5);
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "noban") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") != strings.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp
index de877fd167..bbdf1ef830 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/setup_common.cpp
@@ -85,8 +85,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+ g_chainstate = MakeUnique<CChainState>();
+ ::ChainstateActive().InitCoinsDB(
+ /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
+ assert(!::ChainstateActive().CanFlushToDisk());
+ ::ChainstateActive().InitCoinsCache();
+ assert(::ChainstateActive().CanFlushToDisk());
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
@@ -113,8 +117,7 @@ TestingSetup::~TestingSetup()
g_connman.reset();
g_banman.reset();
UnloadBlockIndex();
- pcoinsTip.reset();
- pcoinsdbview.reset();
+ g_chainstate.reset();
pblocktree.reset();
}
@@ -122,7 +125,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
// CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
// TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
+ gArgs.ForceSetArg("-segwitheight", "432");
SelectParams(CBaseChainParams::REGTEST);
// Generate a 100-block chain:
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index f99a3748c9..e69ebcc2c3 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -97,7 +97,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
BOOST_CHECK_EQUAL(mempool.size(), 0U);
}
-// Run CheckInputs (using pcoinsTip) on the given transaction, for all script
+// Run CheckInputs (using CoinsTip()) on the given transaction, for all script
// flags. Test that CheckInputs passes for all flags that don't overlap with
// the failing_flags argument, but otherwise fails.
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
@@ -125,7 +125,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -135,13 +135,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(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, 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(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -204,13 +204,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
- BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, 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(CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputs returns true iff DERSIG-enforcing flags are
@@ -227,7 +227,7 @@ 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(pcoinsTip->GetBestBlock() == block.GetHash());
+ BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -272,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx);
- BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -300,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx);
- BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -362,12 +362,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, 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(CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, 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/txdb.cpp b/src/txdb.cpp
index df9851396e..18be07e6db 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -52,7 +52,7 @@ struct CoinEntry {
}
-CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
+CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) : db(ldb_path, nCacheSize, fMemory, fWipe, true)
{
}
diff --git a/src/txdb.h b/src/txdb.h
index c4ece11503..140ce2c7ff 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -48,7 +48,10 @@ class CCoinsViewDB final : public CCoinsView
protected:
CDBWrapper db;
public:
- explicit CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
+ /**
+ * @param[in] ldb_path Location in the filesystem where leveldb data will be stored.
+ */
+ explicit CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
diff --git a/src/txmempool.h b/src/txmempool.h
index 7169e80da2..6e5ba445d3 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -497,7 +497,7 @@ public:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
* that is consistent with current chain tip (`::ChainActive()` and
- * `pcoinsTip`) and is fully populated. Fully populated means that if the
+ * `CoinsTip()`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been
* re-added to the mempool and should be present if they meet size and
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 9edb7dc533..287476c0d3 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -36,12 +36,17 @@ std::string TransactionErrorString(const TransactionError err)
assert(false);
}
+std::string ResolveErrMsg(const std::string& optname, const std::string& strBind)
+{
+ return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
+}
+
std::string AmountHighWarn(const std::string& optname)
{
return strprintf(_("%s is set very high!").translated, optname);
}
-std::string AmountErrMsg(const char* const optname, const std::string& strValue)
+std::string AmountErrMsg(const std::string& optname, const std::string& strValue)
{
return strprintf(_("Invalid amount for -%s=<amount>: '%s'").translated, optname, strValue);
}
diff --git a/src/util/error.h b/src/util/error.h
index 0fd474b962..7777cc0c5d 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -10,7 +10,7 @@
* string functions. Types and functions defined here should not require any
* outside dependencies.
*
- * Error types defined here can be used in different parts of the bitcoin
+ * Error types defined here can be used in different parts of the
* codebase, to avoid the need to write boilerplate code catching and
* translating errors passed across wallet/node/rpc/gui code boundaries.
*/
@@ -32,8 +32,10 @@ enum class TransactionError {
std::string TransactionErrorString(const TransactionError error);
+std::string ResolveErrMsg(const std::string& optname, const std::string& strBind);
+
std::string AmountHighWarn(const std::string& optname);
-std::string AmountErrMsg(const char* const optname, const std::string& strValue);
+std::string AmountErrMsg(const std::string& optname, const std::string& strValue);
#endif // BITCOIN_UTIL_ERROR_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index f8bcc45a6a..c925dec253 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -748,8 +748,9 @@ const fs::path &GetDataDir(bool fNetSpecific)
// this function
if (!path.empty()) return path;
- if (gArgs.IsArgSet("-datadir")) {
- path = fs::system_complete(gArgs.GetArg("-datadir", ""));
+ std::string datadir = gArgs.GetArg("-datadir", "");
+ if (!datadir.empty()) {
+ path = fs::system_complete(datadir);
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -768,6 +769,12 @@ const fs::path &GetDataDir(bool fNetSpecific)
return path;
}
+bool CheckDataDirOption()
+{
+ std::string datadir = gArgs.GetArg("-datadir", "");
+ return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
+}
+
void ClearDatadirCache()
{
LOCK(csPathCached);
@@ -937,7 +944,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
// If datadir is changed in .conf file:
ClearDatadirCache();
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str());
return false;
}
@@ -1205,6 +1212,9 @@ int64_t GetStartupTime()
fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
{
+ if (path.is_absolute()) {
+ return path;
+ }
return fs::absolute(path, GetDataDir(net_specific));
}
diff --git a/src/util/system.h b/src/util/system.h
index 75e8096826..908a3c407d 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -71,6 +71,8 @@ fs::path GetDefaultDataDir();
// The blocks directory is always net specific.
const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
+// Return true if -datadir option points to a valid directory or is not specified.
+bool CheckDataDirOption();
/** Tests only */
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
diff --git a/src/validation.cpp b/src/validation.cpp
index b4677df62f..74f68a3047 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -82,11 +82,17 @@ namespace {
BlockManager g_blockman;
} // anon namespace
-static CChainState g_chainstate(g_blockman);
+std::unique_ptr<CChainState> g_chainstate;
-CChainState& ChainstateActive() { return g_chainstate; }
+CChainState& ChainstateActive() {
+ assert(g_chainstate);
+ return *g_chainstate;
+}
-CChain& ChainActive() { return g_chainstate.m_chain; }
+CChain& ChainActive() {
+ assert(g_chainstate);
+ return g_chainstate->m_chain;
+}
/**
* Mutex to guard access to validation specific variables, such as reading
@@ -173,8 +179,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
return chain.Genesis();
}
-std::unique_ptr<CCoinsViewDB> pcoinsdbview;
-std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
// See definition for documentation
@@ -260,8 +264,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
- // pcoinsTip contains the UTXO set for ::ChainActive().Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
+ // CoinsTip() contains the UTXO set for ::ChainActive().Tip()
+ CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -310,7 +314,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
-static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
+static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
{
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
@@ -320,7 +325,7 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
for (const COutPoint& removed : vNoSpendsRemaining)
- pcoinsTip->Uncache(removed);
+ ::ChainstateActive().CoinsTip().Uncache(removed);
}
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -382,7 +387,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(pcoinsTip.get(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
@@ -414,7 +419,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout);
+ const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
assert(!coinFromDisk.IsSpent());
assert(coinFromDisk.out == coin.out);
}
@@ -514,23 +519,24 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CCoinsViewCache view(&dummy);
LockPoints lp;
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
+ CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+ CCoinsViewMemPool viewMemPool(&coins_cache, pool);
view.SetBackend(viewMemPool);
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
- if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
+ if (!coins_cache.HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
// Note: this call may add txin.prevout to the coins cache
- // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
+ // (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed
// later (via coins_to_uncache) if this tx turns out to be invalid.
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for outputs
- if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
+ if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) {
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
}
}
@@ -860,7 +866,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
// (`CCoinsViewCache::cacheCoins`).
for (const COutPoint& hashTx : coins_to_uncache)
- pcoinsTip->Uncache(hashTx);
+ ::ChainstateActive().CoinsTip().Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
@@ -1040,6 +1046,40 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
+CoinsViews::CoinsViews(
+ std::string ldb_name,
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe) : m_dbview(
+ GetDataDir() / ldb_name, cache_size_bytes, in_memory, should_wipe),
+ m_catcherview(&m_dbview) {}
+
+void CoinsViews::InitCache()
+{
+ m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
+}
+
+// NOTE: for now m_blockman is set to a global, but this will be changed
+// in a future commit.
+CChainState::CChainState() : m_blockman(g_blockman) {}
+
+
+void CChainState::InitCoinsDB(
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe,
+ std::string leveldb_name)
+{
+ m_coins_views = MakeUnique<CoinsViews>(
+ leveldb_name, cache_size_bytes, in_memory, should_wipe);
+}
+
+void CChainState::InitCoinsCache()
+{
+ assert(m_coins_views != nullptr);
+ m_coins_views->InitCache();
+}
+
// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
// is a performance-related implementation detail. This function must be marked
// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
@@ -1608,7 +1648,7 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_
// environment. See test/functional/p2p-segwit.py.
static bool IsScriptWitnessEnabled(const Consensus::Params& params)
{
- return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0;
+ return params.SegwitHeight != std::numeric_limits<int>::max();
}
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
@@ -1644,12 +1684,13 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
- // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
- if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ // Start enforcing BIP112 (CHECKSEQUENCEVERIFY)
+ if (pindex->nHeight >= consensusparams.CSVHeight) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
- if (IsNullDummyEnabled(pindex->pprev, consensusparams)) {
+ // Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit)
+ if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -1834,9 +1875,9 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
}
}
- // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
+ // Start enforcing BIP68 (sequence locks)
int nLockTimeFlags = 0;
- if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ if (pindex->nHeight >= chainparams.GetConsensus().CSVHeight) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
@@ -1981,6 +2022,7 @@ bool CChainState::FlushStateToDisk(
{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
+ assert(this->CanFlushToDisk());
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
std::set<int> setFilesToPrune;
@@ -2014,7 +2056,7 @@ bool CChainState::FlushStateToDisk(
nLastFlush = nNow;
}
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
+ int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
@@ -2058,17 +2100,17 @@ bool CChainState::FlushStateToDisk(
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
- if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) {
+ if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) {
+ if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
// Flush the chainstate (which may refer to block index entries).
- if (!pcoinsTip->Flush())
+ if (!CoinsTip().Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
full_flush_completed = true;
@@ -2120,7 +2162,9 @@ static void AppendWarning(std::string& res, const std::string& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
+void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
// New best block
mempool.AddTransactionsUpdated(1);
@@ -2162,7 +2206,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
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), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize());
if (!warningMessages.empty())
LogPrintf(" warning='%s'", warningMessages); /* Continued */
LogPrintf("\n");
@@ -2191,7 +2235,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
- CCoinsViewCache view(pcoinsTip.get());
+ CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
@@ -2319,7 +2363,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{
- CCoinsViewCache view(pcoinsTip.get());
+ CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
@@ -2506,7 +2550,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
}
- mempool.check(pcoinsTip.get());
+ mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -3045,14 +3089,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
- LOCK(cs_main);
- return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
-}
-
-bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
-{
- LOCK(cs_main);
- return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
+ int height = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
+ return (height >= params.SegwitHeight);
}
// Compute at which vout of the block's coinbase transaction the witness
@@ -3087,7 +3125,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.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
+ if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
if (commitpos == -1) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
@@ -3185,9 +3223,9 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
{
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
- // Start enforcing BIP113 (Median Time Past) using versionbits logic.
+ // Start enforcing BIP113 (Median Time Past).
int nLockTimeFlags = 0;
- if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ if (nHeight >= consensusParams.CSVHeight) {
assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3222,7 +3260,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// {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 (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE) {
+ if (nHeight >= consensusParams.SegwitHeight) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
bool malleated = false;
@@ -3508,7 +3546,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
- CCoinsViewCache viewNew(pcoinsTip.get());
+ CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
@@ -3861,12 +3899,14 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
- assert(!pcoinsTip->GetBestBlock().IsNull()); // Never called when the coins view is empty
+ const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+ assert(!coins_cache.GetBestBlock().IsNull()); // Never called when the coins view is empty
- if (::ChainActive().Tip() && ::ChainActive().Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
+ if (::ChainActive().Tip() &&
+ ::ChainActive().Tip()->GetBlockHash() == coins_cache.GetBestBlock()) return true;
// Load pointer to end of best chain
- CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
+ CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock());
if (!pindex) {
return false;
}
@@ -3943,7 +3983,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
diff --git a/src/validation.h b/src/validation.h
index d747fdbf27..99850f71d9 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -19,6 +19,7 @@
#include <script/script_error.h>
#include <sync.h>
#include <txmempool.h> // For CTxMemPool::cs
+#include <txdb.h>
#include <versionbits.h>
#include <algorithm>
@@ -37,7 +38,6 @@ class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
class CChainParams;
-class CCoinsViewDB;
class CInv;
class CConnman;
class CScriptCheck;
@@ -50,10 +50,6 @@ struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
-/** Default for -whitelistrelay. */
-static const bool DEFAULT_WHITELISTRELAY = true;
-/** Default for -whitelistforcerelay. */
-static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
@@ -383,12 +379,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Check whether witness commitments are required for block. */
+/** 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);
-/** Check whether NULLDUMMY (BIP 147) has activated. */
-bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
-
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
@@ -506,6 +500,41 @@ public:
};
/**
+ * A convenience class for constructing the CCoinsView* hierarchy used
+ * to facilitate access to the UTXO set.
+ *
+ * This class consists of an arrangement of layered CCoinsView objects,
+ * preferring to store and retrieve coins in memory via `m_cacheview` but
+ * ultimately falling back on cache misses to the canonical store of UTXOs on
+ * disk, `m_dbview`.
+ */
+class CoinsViews {
+
+public:
+ //! The lowest level of the CoinsViews cache hierarchy sits in a leveldb database on disk.
+ //! All unspent coins reside in this store.
+ CCoinsViewDB m_dbview GUARDED_BY(cs_main);
+
+ //! This view wraps access to the leveldb instance and handles read errors gracefully.
+ CCoinsViewErrorCatcher m_catcherview GUARDED_BY(cs_main);
+
+ //! This is the top layer of the cache hierarchy - it keeps as many coins in memory as
+ //! can fit per the dbcache setting.
+ std::unique_ptr<CCoinsViewCache> m_cacheview GUARDED_BY(cs_main);
+
+ //! This constructor initializes CCoinsViewDB and CCoinsViewErrorCatcher instances, but it
+ //! *does not* create a CCoinsViewCache instance by default. This is done separately because the
+ //! presence of the cache has implications on whether or not we're allowed to flush the cache's
+ //! state to disk, which should not be done until the health of the database is verified.
+ //!
+ //! All arguments forwarded onto CCoinsViewDB.
+ CoinsViews(std::string ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe);
+
+ //! Initialize the CCoinsViewCache member.
+ void InitCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+};
+
+/**
* CChainState stores and provides an API to update our local knowledge of the
* current best chain.
*
@@ -553,12 +582,39 @@ private:
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
+ //! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
+ std::unique_ptr<CoinsViews> m_coins_views;
+
public:
- CChainState(BlockManager& blockman) : m_blockman(blockman) { }
+ CChainState(BlockManager& blockman) : m_blockman(blockman) {}
+ CChainState();
+
+ /**
+ * Initialize the CoinsViews UTXO set database management data structures. The in-memory
+ * cache is initialized separately.
+ *
+ * All parameters forwarded to CoinsViews.
+ */
+ void InitCoinsDB(
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe,
+ std::string leveldb_name = "chainstate");
+
+ //! Initialize the in-memory coins cache (to be done after the health of the on-disk database
+ //! is verified).
+ void InitCoinsCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ //! @returns whether or not the CoinsViews object has been fully initialized and we can
+ //! safely flush this object to disk.
+ bool CanFlushToDisk() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+ return m_coins_views && m_coins_views->m_cacheview;
+ }
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
+
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
@@ -566,6 +622,29 @@ public:
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
+ //! @returns A reference to the in-memory cache of the UTXO set.
+ CCoinsViewCache& CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ assert(m_coins_views->m_cacheview);
+ return *m_coins_views->m_cacheview.get();
+ }
+
+ //! @returns A reference to the on-disk UTXO set database.
+ CCoinsViewDB& CoinsDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ return m_coins_views->m_dbview;
+ }
+
+ //! @returns A reference to a wrapped view of the in-memory UTXO set that
+ //! handles disk read errors gracefully.
+ CCoinsViewErrorCatcher& CoinsErrorCatcher() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ return m_coins_views->m_catcherview;
+ }
+
+ //! Destructs all objects related to accessing the UTXO set.
+ void ResetCoinsViews() { m_coins_views.reset(); }
+
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with
@@ -597,7 +676,7 @@ public:
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- // Block disconnection on our pcoinsTip:
+ // Apply the effects of a block disconnection on the UTXO set.
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
// Manual block validity manipulation:
@@ -659,11 +738,10 @@ CChain& ChainActive();
/** @returns the global block index map. */
BlockMap& BlockIndex();
-/** Global variable that points to the coins database (protected by cs_main) */
-extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
-
-/** Global variable that points to the active CCoinsView (protected by cs_main) */
-extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
+// Most often ::ChainstateActive() should be used instead of this, but some code
+// may not be able to assume that this has been initialized yet and so must use it
+// directly, e.g. init.cpp.
+extern std::unique_ptr<CChainState> g_chainstate;
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 3f297c0ebb..2285579cd9 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -94,7 +94,6 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
-// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const
{
BIP9Stats stats = {};
diff --git a/src/versionbits.h b/src/versionbits.h
index cdc947cd9e..d8dda7d95b 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -17,12 +17,17 @@ static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
/** Total bits available for versionbits */
static const int32_t VERSIONBITS_NUM_BITS = 29;
+/** BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages.
+ * State transitions happen during retarget period if conditions are met
+ * In case of reorg, transitions can go backward. Without transition, state is
+ * inherited between periods. All blocks of a period share the same state.
+ */
enum class ThresholdState {
- DEFINED,
- STARTED,
- LOCKED_IN,
- ACTIVE,
- FAILED,
+ DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
+ STARTED, // For blocks past the starttime.
+ LOCKED_IN, // For one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion.
+ ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state)
+ FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
};
// A map that gives the state for blocks whose height is a multiple of Period().
@@ -30,11 +35,17 @@ enum class ThresholdState {
// will either be nullptr or a block with (height + 1) % Period() == 0.
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
+/** Display status of an in-progress BIP9 softfork */
struct BIP9Stats {
+ /** Length of blocks of the BIP9 signalling period */
int period;
+ /** Number of blocks with the version bit set required to activate the softfork */
int threshold;
+ /** Number of blocks elapsed since the beginning of the current period */
int elapsed;
+ /** Number of blocks with the version bit set since the beginning of the current period */
int count;
+ /** False if there are not enough blocks left in this period to pass activation threshold */
bool possible;
};
@@ -50,12 +61,17 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
+ /** Returns the numerical statistics of an in-progress BIP9 softfork in the current period */
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
- // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
+ /** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
+ * Caches state from first block of period. */
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
+ /** Returns the height since when the ThresholdState has started for pindex A based on parent pindexPrev B, all blocks of a period share the same */
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
{
ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS];
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
index ecf3482927..82df92ac90 100644
--- a/src/versionbitsinfo.cpp
+++ b/src/versionbitsinfo.cpp
@@ -11,12 +11,4 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
},
- {
- /*.name =*/ "csv",
- /*.gbt_force =*/ true,
- },
- {
- /*.name =*/ "segwit",
- /*.gbt_force =*/ true,
- }
};
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index a905cc0c55..7707d6233b 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1098,9 +1098,10 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
const std::string& descriptor = data["desc"].get_str();
FlatSigningProvider keys;
- auto parsed_desc = Parse(descriptor, keys, /* require_checksum = */ true);
+ std::string error;
+ auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
if (!parsed_desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor is invalid");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
have_solving_data = parsed_desc->IsSolvable();
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index cbab73d612..30e767e489 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -52,6 +52,23 @@ static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& pa
return avoid_reuse;
}
+
+/** Used by RPC commands that have an include_watchonly parameter.
+ * We default to true for watchonly wallets if include_watchonly isn't
+ * explicitly set.
+ */
+static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet)
+{
+ if (include_watchonly.isNull()) {
+ // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
+ return pwallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ }
+
+ // otherwise return whatever include_watchonly was set to
+ return include_watchonly.get_bool();
+}
+
+
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
bool HaveKey(const CWallet& wallet, const CKey& key)
{
@@ -710,7 +727,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
{"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also include balance in watch-only addresses (see 'importaddress')"},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
@@ -743,10 +760,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
min_depth = request.params[1].get_int();
}
- bool include_watchonly = false;
- if (!request.params[2].isNull() && request.params[2].get_bool()) {
- include_watchonly = true;
- }
+ bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
@@ -1023,9 +1037,10 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
fIncludeEmpty = params[1].get_bool();
isminefilter filter = ISMINE_SPENDABLE;
- if(!params[2].isNull())
- if(params[2].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(params[2], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
bool has_filtered_address = false;
CTxDestination filtered_address = CNoDestination();
@@ -1169,7 +1184,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
{"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include addresses that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
@@ -1220,7 +1235,7 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
{"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include labels that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
},
RPCResult{
"[\n"
@@ -1361,7 +1376,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
" with the specified label, or \"*\" to disable filtering and return all transactions."},
{"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"},
{"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
"[\n"
@@ -1424,9 +1439,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (!request.params[2].isNull())
nFrom = request.params[2].get_int();
isminefilter filter = ISMINE_SPENDABLE;
- if(!request.params[3].isNull())
- if(request.params[3].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
@@ -1492,7 +1508,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
{
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
{"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
{"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
" (not guaranteed to work on pruned nodes)"},
},
@@ -1569,8 +1585,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
}
}
- if (!request.params[2].isNull() && request.params[2].get_bool()) {
- filter = filter | ISMINE_WATCH_ONLY;
+ if (ParseIncludeWatchonly(request.params[2], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
}
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
@@ -1632,7 +1648,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses in balance calculation and details[]"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses in balance calculation and details[]"},
},
RPCResult{
"{\n"
@@ -1687,9 +1703,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
uint256 hash(ParseHashV(request.params[0], "txid"));
isminefilter filter = ISMINE_SPENDABLE;
- if(!request.params[1].isNull())
- if(request.params[1].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(request.params[1], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
UniValue entry(UniValue::VOBJ);
auto it = pwallet->mapWallet.find(hash);
@@ -3015,8 +3032,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
- if (options.exists("includeWatching"))
- coinControl.fAllowWatchOnly = options["includeWatching"].get_bool();
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
if (options.exists("lockUnspents"))
lockUnspents = options["lockUnspents"].get_bool();
@@ -3048,6 +3064,9 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
}
+ } else {
+ // if options is null and not a bool
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet);
}
if (tx.vout.size() == 0)
@@ -3102,7 +3121,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
@@ -4047,7 +4066,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"