aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/addrman.cpp10
-rw-r--r--src/arith_uint256.h3
-rw-r--r--src/attributes.h22
-rw-r--r--src/base58.h10
-rw-r--r--src/bench/base58.cpp2
-rw-r--r--src/bench/coin_selection.cpp29
-rw-r--r--src/bench/duplicate_inputs.cpp100
-rw-r--r--src/bench/prevector.cpp10
-rw-r--r--src/bitcoin-cli.cpp5
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/blockencodings.h9
-rw-r--r--src/chain.h9
-rw-r--r--src/compat.h10
-rw-r--r--src/core_io.h7
-rw-r--r--src/hash.h10
-rw-r--r--src/httpserver.cpp44
-rw-r--r--src/init.cpp23
-rw-r--r--src/interfaces/node.cpp1
-rw-r--r--src/miner.cpp5
-rw-r--r--src/net.cpp25
-rw-r--r--src/net.h56
-rw-r--r--src/net_processing.cpp4
-rw-r--r--src/netaddress.cpp11
-rw-r--r--src/netaddress.h3
-rw-r--r--src/netbase.cpp4
-rw-r--r--src/outputtype.h3
-rw-r--r--src/prevector.h8
-rw-r--r--src/primitives/transaction.h8
-rw-r--r--src/qt/askpassphrasedialog.cpp17
-rw-r--r--src/qt/bitcoin.cpp25
-rw-r--r--src/qt/bitcoinamountfield.cpp67
-rw-r--r--src/qt/bitcoinamountfield.h9
-rw-r--r--src/qt/bitcoingui.cpp12
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/coincontroldialog.cpp20
-rw-r--r--src/qt/coincontroldialog.h9
-rw-r--r--src/qt/forms/coincontroldialog.ui2
-rw-r--r--src/qt/forms/sendcoinsdialog.ui41
-rw-r--r--src/qt/guiutil.cpp14
-rw-r--r--src/qt/overviewpage.cpp32
-rwxr-xr-xsrc/qt/res/movies/makespinner.sh2
-rw-r--r--src/qt/sendcoinsdialog.cpp37
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/splashscreen.cpp8
-rw-r--r--src/qt/splashscreen.h7
-rw-r--r--src/qt/test/util.h2
-rw-r--r--src/qt/utilitydialog.cpp7
-rw-r--r--src/rest.cpp5
-rw-r--r--src/rpc/blockchain.cpp366
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp179
-rw-r--r--src/rpc/misc.cpp130
-rw-r--r--src/rpc/net.cpp131
-rw-r--r--src/rpc/rawtransaction.cpp439
-rw-r--r--src/rpc/server.cpp40
-rw-r--r--src/rpc/util.cpp303
-rw-r--r--src/rpc/util.h93
-rw-r--r--src/script/descriptor.cpp506
-rw-r--r--src/script/descriptor.h33
-rw-r--r--src/script/sign.cpp13
-rw-r--r--src/script/sign.h8
-rw-r--r--src/streams.h4
-rw-r--r--src/test/addrman_tests.cpp2
-rw-r--r--src/test/blockencodings_tests.cpp45
-rw-r--r--src/test/coins_tests.cpp9
-rw-r--r--src/test/dbwrapper_tests.cpp8
-rw-r--r--src/test/descriptor_tests.cpp50
-rw-r--r--src/test/getarg_tests.cpp2
-rw-r--r--src/test/net_tests.cpp38
-rw-r--r--src/test/rpc_tests.cpp3
-rw-r--r--src/test/serialize_tests.cpp2
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/util_tests.cpp46
-rw-r--r--src/uint256.h11
-rw-r--r--src/util/moneystr.h11
-rw-r--r--src/util/strencodings.cpp2
-rw-r--r--src/util/strencodings.h18
-rw-r--r--src/util/system.cpp43
-rw-r--r--src/util/system.h15
-rw-r--r--src/validation.cpp40
-rw-r--r--src/validation.h6
-rw-r--r--src/wallet/crypter.cpp2
-rw-r--r--src/wallet/db.cpp49
-rw-r--r--src/wallet/db.h20
-rw-r--r--src/wallet/rpcdump.cpp205
-rw-r--r--src/wallet/rpcwallet.cpp1026
-rw-r--r--src/wallet/test/wallet_tests.cpp44
-rw-r--r--src/wallet/wallet.cpp19
-rw-r--r--src/wallet/wallet.h3
-rw-r--r--src/zmq/zmqrpc.cpp6
94 files changed, 2990 insertions, 1744 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8dd0d31839..09daaebd23 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -95,6 +95,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
+ attributes.h \
base58.h \
bech32.h \
bloom.h \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 544092520f..5e787ca222 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -18,6 +18,7 @@ bench_bench_bitcoin_SOURCES = \
bench/block_assemble.cpp \
bench/checkblock.cpp \
bench/checkqueue.cpp \
+ bench/duplicate_inputs.cpp \
bench/examples.cpp \
bench/rollingbloom.cpp \
bench/crypto_hash.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a31852c94f..8ce7562434 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -132,7 +132,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 093b263ab3..44328c3056 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -11,22 +11,22 @@
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash();
- uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
+ uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
}
int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const
{
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetCheapHash();
- uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetCheapHash();
+ uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
}
int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
diff --git a/src/arith_uint256.h b/src/arith_uint256.h
index 5cc273be27..bd0360087d 100644
--- a/src/arith_uint256.h
+++ b/src/arith_uint256.h
@@ -8,6 +8,7 @@
#include <assert.h>
#include <cstring>
+#include <limits>
#include <stdexcept>
#include <stdint.h>
#include <string>
@@ -189,7 +190,7 @@ public:
{
// prefix operator
int i = 0;
- while (i < WIDTH && --pn[i] == (uint32_t)-1)
+ while (i < WIDTH && --pn[i] == std::numeric_limits<uint32_t>::max())
i++;
return *this;
}
diff --git a/src/attributes.h b/src/attributes.h
new file mode 100644
index 0000000000..45099bd8b8
--- /dev/null
+++ b/src/attributes.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// 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.
+
+#ifndef BITCOIN_ATTRIBUTES_H
+#define BITCOIN_ATTRIBUTES_H
+
+#if defined(__has_cpp_attribute)
+# if __has_cpp_attribute(nodiscard)
+# define NODISCARD [[nodiscard]]
+# endif
+#endif
+#ifndef NODISCARD
+# if defined(_MSC_VER) && _MSC_VER >= 1700
+# define NODISCARD _Check_return_
+# else
+# define NODISCARD __attribute__((warn_unused_result))
+# endif
+#endif
+
+#endif // BITCOIN_ATTRIBUTES_H
diff --git a/src/base58.h b/src/base58.h
index 9d3f90652e..d6e0299a1e 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,6 +14,8 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
+#include <attributes.h>
+
#include <string>
#include <vector>
@@ -33,13 +35,13 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch);
* return true if decoding is successful.
* psz cannot be nullptr.
*/
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
-bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
/**
* Encode a byte vector into a base58-encoded string, including checksum
@@ -50,12 +52,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
* Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
#endif // BITCOIN_BASE58_H
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index a555376e40..e7702ec461 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -49,7 +49,7 @@ static void Base58Decode(benchmark::State& state)
const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem";
std::vector<unsigned char> vch;
while (state.KeepRunning()) {
- DecodeBase58(addr, vch);
+ (void) DecodeBase58(addr, vch);
}
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 8552ed34fd..74641191a1 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -4,25 +4,19 @@
#include <bench/bench.h>
#include <interfaces/chain.h>
-#include <wallet/wallet.h>
#include <wallet/coinselection.h>
+#include <wallet/wallet.h>
#include <set>
-static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<OutputGroup>& groups)
+static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs)
{
- int nInput = 0;
-
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- tx.vout.resize(nInput + 1);
- tx.vout[nInput].nValue = nValue;
- CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
-
- int nAge = 6 * 24;
- COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
+ tx.vout.resize(1);
+ tx.vout[0].nValue = nValue;
+ wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -36,14 +30,21 @@ static void CoinSelection(benchmark::State& state)
{
auto chain = interfaces::MakeChain();
const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
// Add coins.
- std::vector<OutputGroup> groups;
for (int i = 0; i < 1000; ++i) {
- addCoin(1000 * COIN, wallet, groups);
+ addCoin(1000 * COIN, wallet, wtxs);
+ }
+ addCoin(3 * COIN, wallet, wtxs);
+
+ // Create groups
+ std::vector<OutputGroup> groups;
+ for (const auto& wtx : wtxs) {
+ COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
+ groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
}
- addCoin(3 * COIN, wallet, groups);
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
new file mode 100644
index 0000000000..e0854e2c62
--- /dev/null
+++ b/src/bench/duplicate_inputs.cpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2011-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 <bench/bench.h>
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <miner.h>
+#include <policy/policy.h>
+#include <pow.h>
+#include <scheduler.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <util/time.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+#include <boost/thread.hpp>
+
+#include <list>
+#include <vector>
+
+
+static void DuplicateInputs(benchmark::State& state)
+{
+ const CScript SCRIPT_PUB{CScript(OP_TRUE)};
+
+ // Switch to regtest so we can mine faster
+ // Also segwit is active, so we can include witness transactions
+ SelectParams(CBaseChainParams::REGTEST);
+
+ InitScriptExecutionCache();
+
+ boost::thread_group thread_group;
+ CScheduler scheduler;
+ const CChainParams& chainparams = Params();
+ {
+ ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+
+ thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ LoadGenesisBlock(chainparams);
+ CValidationState cvstate;
+ ActivateBestChain(cvstate, chainparams);
+ assert(::chainActive.Tip() != nullptr);
+ const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
+ assert(witness_enabled);
+ }
+
+ CBlock block{};
+ CMutableTransaction coinbaseTx{};
+ CMutableTransaction naughtyTx{};
+
+ CBlockIndex* pindexPrev = ::chainActive.Tip();
+ assert(pindexPrev != nullptr);
+ block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
+ block.nNonce = 0;
+ auto nHeight = pindexPrev->nHeight + 1;
+
+ // Make a coinbase TX
+ coinbaseTx.vin.resize(1);
+ coinbaseTx.vin[0].prevout.SetNull();
+ coinbaseTx.vout.resize(1);
+ coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB;
+ coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
+ coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
+
+
+ naughtyTx.vout.resize(1);
+ naughtyTx.vout[0].nValue = 0;
+ naughtyTx.vout[0].scriptPubKey = SCRIPT_PUB;
+
+ uint64_t n_inputs = (((MAX_BLOCK_SERIALIZED_SIZE / WITNESS_SCALE_FACTOR) - (CTransaction(coinbaseTx).GetTotalSize() + CTransaction(naughtyTx).GetTotalSize())) / 41) - 100;
+ for (uint64_t x = 0; x < (n_inputs - 1); ++x) {
+ naughtyTx.vin.emplace_back(GetRandHash(), 0, CScript(), 0);
+ }
+ naughtyTx.vin.emplace_back(naughtyTx.vin.back());
+
+ block.vtx.push_back(MakeTransactionRef(std::move(coinbaseTx)));
+ block.vtx.push_back(MakeTransactionRef(std::move(naughtyTx)));
+
+ block.hashMerkleRoot = BlockMerkleRoot(block);
+
+ while (state.KeepRunning()) {
+ CValidationState cvstate{};
+ assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
+ assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
+ }
+
+ thread_group.interrupt_all();
+ thread_group.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+}
+
+BENCHMARK(DuplicateInputs, 10);
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 8cc404b9e2..00e5d7e7a0 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -2,13 +2,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <compat.h>
#include <prevector.h>
#include <serialize.h>
#include <streams.h>
+#include <type_traits>
#include <bench/bench.h>
+// GCC 4.8 is missing some C++11 type_traits,
+// https://www.gnu.org/software/gcc/gcc-5/changes.html
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor
+#else
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible
+#endif
+
struct nontrivial_t {
int x;
nontrivial_t() :x(-1) {}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index dc1b46faf1..7cdf61ad35 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -52,7 +52,7 @@ static void SetupCliArgs()
gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
@@ -492,9 +492,6 @@ static int CommandLineRPC(int argc, char *argv[])
}
} while (fWait);
}
- catch (const boost::thread_interrupted&) {
- throw;
- }
catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index bc91ca3641..2e41adc276 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -820,10 +820,6 @@ static int CommandLineRawTx(int argc, char* argv[])
OutputTx(tx);
}
-
- catch (const boost::thread_interrupted&) {
- throw;
- }
catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 1306ba3821..dde75c1b12 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -81,7 +81,7 @@ static bool AppInit(int argc, char* argv[])
if (gArgs.IsArgSet("-version"))
{
- strUsage += FormatParagraph(LicenseInfo());
+ strUsage += FormatParagraph(LicenseInfo()) + "\n";
}
else
{
diff --git a/src/blockencodings.h b/src/blockencodings.h
index fad1f56f54..0c2b83ebcf 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -52,12 +52,12 @@ public:
}
}
- uint16_t offset = 0;
+ int32_t offset = 0;
for (size_t j = 0; j < indexes.size(); j++) {
- if (uint64_t(indexes[j]) + uint64_t(offset) > std::numeric_limits<uint16_t>::max())
+ if (int32_t(indexes[j]) + offset > std::numeric_limits<uint16_t>::max())
throw std::ios_base::failure("indexes overflowed 16 bits");
indexes[j] = indexes[j] + offset;
- offset = indexes[j] + 1;
+ offset = int32_t(indexes[j]) + 1;
}
} else {
for (size_t i = 0; i < indexes.size(); i++) {
@@ -186,6 +186,9 @@ public:
READWRITE(prefilledtxn);
+ if (BlockTxCount() > std::numeric_limits<uint16_t>::max())
+ throw std::ios_base::failure("indexes overflowed 16 bits");
+
if (ser_action.ForRead())
FillShortTxIDSelector();
}
diff --git a/src/chain.h b/src/chain.h
index 1c0c5a7e69..3b786664af 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -294,6 +294,15 @@ public:
return *phashBlock;
}
+ /**
+ * Check whether this block's and all previous blocks' transactions have been
+ * downloaded (and stored to disk) at some point.
+ *
+ * Does not imply the transactions are consensus-valid (ConnectTip might fail)
+ * Does not imply the transactions are still stored on disk. (IsBlockPruned might return true)
+ */
+ bool HaveTxsDownloaded() const { return nChainTx != 0; }
+
int64_t GetBlockTime() const
{
return (int64_t)nTime;
diff --git a/src/compat.h b/src/compat.h
index d228611160..049579c365 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -10,16 +10,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <type_traits>
-
-// GCC 4.8 is missing some C++11 type_traits,
-// https://www.gnu.org/software/gcc/gcc-5/changes.html
-#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
-#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor
-#else
-#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible
-#endif
-
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
diff --git a/src/core_io.h b/src/core_io.h
index 2c3b64d81e..6f87161f46 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -6,6 +6,7 @@
#define BITCOIN_CORE_IO_H
#include <amount.h>
+#include <attributes.h>
#include <string>
#include <vector>
@@ -22,8 +23,8 @@ class UniValue;
// core_read.cpp
CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
-bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
-bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
+NODISCARD bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
+NODISCARD bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
/**
@@ -36,7 +37,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
*/
bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
-bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
+NODISCARD bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
diff --git a/src/hash.h b/src/hash.h
index 6acab0b161..c295568a3e 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
+#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
#include <prevector.h>
@@ -138,6 +139,15 @@ public:
return result;
}
+ /**
+ * Returns the first 64 bits from the resulting hash.
+ */
+ inline uint64_t GetCheapHash() {
+ unsigned char result[CHash256::OUTPUT_SIZE];
+ ctx.Finalize(result);
+ return ReadLE64(result);
+ }
+
template<typename T>
CHashWriter& operator<<(const T& obj) {
// Serialize to this stream
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 91ebc4680c..cb8578927a 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -10,6 +10,7 @@
#include <util/strencodings.h>
#include <netbase.h>
#include <rpc/protocol.h> // For HTTP status codes
+#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
@@ -21,7 +22,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
-#include <future>
#include <event2/thread.h>
#include <event2/buffer.h>
@@ -300,9 +300,12 @@ static bool HTTPBindAddresses(struct evhttp* http)
std::vector<std::pair<std::string, uint16_t> > endpoints;
// Determine what addresses to bind to
- if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs
+ if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
endpoints.push_back(std::make_pair("::1", http_port));
endpoints.push_back(std::make_pair("127.0.0.1", http_port));
+ if (gArgs.IsArgSet("-rpcallowip")) {
+ LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n");
+ }
if (gArgs.IsArgSet("-rpcbind")) {
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
}
@@ -313,9 +316,6 @@ static bool HTTPBindAddresses(struct evhttp* http)
SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port));
}
- } else { // No specific bind address specified, bind to any
- endpoints.push_back(std::make_pair("::", http_port));
- endpoints.push_back(std::make_pair("0.0.0.0", http_port));
}
// Bind addresses
@@ -323,6 +323,10 @@ static bool HTTPBindAddresses(struct evhttp* http)
LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second);
evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
if (bind_handle) {
+ CNetAddr addr;
+ if (i->first.empty() || (LookupHost(i->first.c_str(), addr, false) && addr.IsBindAny())) {
+ LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n");
+ }
boundSockets.push_back(bind_handle);
} else {
LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
@@ -417,7 +421,6 @@ bool UpdateHTTPServerLogging(bool enable) {
}
std::thread threadHTTP;
-std::future<bool> threadResult;
static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
@@ -425,9 +428,7 @@ void StartHTTPServer()
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
- std::packaged_task<bool(event_base*)> task(ThreadHTTP);
- threadResult = task.get_future();
- threadHTTP = std::thread(std::move(task), eventBase);
+ threadHTTP = std::thread(ThreadHTTP, eventBase);
for (int i = 0; i < rpcThreads; i++) {
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
@@ -438,10 +439,6 @@ void InterruptHTTPServer()
{
LogPrint(BCLog::HTTP, "Interrupting HTTP server\n");
if (eventHTTP) {
- // Unlisten sockets
- for (evhttp_bound_socket *socket : boundSockets) {
- evhttp_del_accept_socket(eventHTTP, socket);
- }
// Reject requests on current connections
evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
}
@@ -461,20 +458,14 @@ void StopHTTPServer()
delete workQueue;
workQueue = nullptr;
}
+ // Unlisten sockets, these are what make the event loop running, which means
+ // that after this and all connections are closed the event loop will quit.
+ for (evhttp_bound_socket *socket : boundSockets) {
+ evhttp_del_accept_socket(eventHTTP, socket);
+ }
+ boundSockets.clear();
if (eventBase) {
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
- // Exit the event loop as soon as there are no active events.
- event_base_loopexit(eventBase, nullptr);
- // Give event loop a few seconds to exit (to send back last RPC responses), then break it
- // Before this was solved with event_base_loopexit, but that didn't work as expected in
- // at least libevent 2.0.21 and always introduced a delay. In libevent
- // master that appears to be solved, so in the future that solution
- // could be used again (if desirable).
- // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990)
- if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
- LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n");
- event_base_loopbreak(eventBase);
- }
threadHTTP.join();
}
if (eventHTTP) {
@@ -579,6 +570,9 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
{
assert(!replySent && req);
+ if (ShutdownRequested()) {
+ WriteHeader("Connection", "close");
+ }
// Send event to main http thread to send reply message
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
diff --git a/src/init.cpp b/src/init.cpp
index 3ab97be329..b1fa8fc695 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -403,6 +403,7 @@ void SetupServerArgs()
gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), true, OptionsCategory::CONNECTION);
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", false, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
@@ -500,7 +501,7 @@ void SetupServerArgs()
gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::RPC);
@@ -803,7 +804,15 @@ void InitParameterInteraction()
// Warn if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
- gArgs.WarnForSectionOnlyArgs();
+ std::string network = gArgs.GetChainName();
+ for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ InitWarning(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
+ }
+
+ // Warn if unrecognized section name are present in the config file.
+ for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ InitWarning(strprintf(_("Section [%s] is not recognized."), section));
+ }
}
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -848,6 +857,7 @@ int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
+int64_t peer_connect_timeout;
} // namespace
@@ -1046,8 +1056,14 @@ bool AppInitParameterInteraction()
}
nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
- if (nConnectTimeout <= 0)
+ if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
+ }
+
+ peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
+ if (peer_connect_timeout <= 0) {
+ return InitError("peertimeout cannot be configured with a negative value.");
+ }
if (gArgs.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
@@ -1685,6 +1701,7 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
+ connOptions.m_peer_connect_timeout = peer_connect_timeout;
for (const std::string& strBind : gArgs.GetArgs("-bind")) {
CService addrBind;
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 1919e16a66..bd7e414ff3 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -35,7 +35,6 @@
#endif
#include <atomic>
-#include <boost/thread/thread.hpp>
#include <univalue.h>
class CWallet;
diff --git a/src/miner.cpp b/src/miner.cpp
index feb86cab66..96c9cd6d2a 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -70,9 +70,8 @@ static BlockAssembler::Options DefaultOptions()
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
BlockAssembler::Options options;
options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
- if (gArgs.IsArgSet("-blockmintxfee")) {
- CAmount n = 0;
- ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n);
+ CAmount n = 0;
+ if (gArgs.IsArgSet("-blockmintxfee") && ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) {
options.blockMinFeeRate = CFeeRate(n);
} else {
options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
diff --git a/src/net.cpp b/src/net.cpp
index 65a308780a..fde85b0f2a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -82,8 +82,8 @@ bool fDiscover = true;
bool fListen = true;
bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
-std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
-static bool vfLimited[NET_MAX] = {};
+std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
+static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
std::string strSubVersion;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
@@ -715,7 +715,10 @@ void CNode::copyStats(CNodeStats &stats)
X(nRecvBytes);
}
X(fWhitelisted);
- X(minFeeFilter);
+ {
+ LOCK(cs_feeFilter);
+ X(minFeeFilter);
+ }
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
@@ -874,16 +877,7 @@ const uint256& CNetMessage::GetMessageHash() const
return data_hash;
}
-
-
-
-
-
-
-
-
-// requires LOCK(cs_vSend)
-size_t CConnman::SocketSendData(CNode *pnode) const
+size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend)
{
auto it = pnode->vSendMsg.begin();
size_t nSentSize = 0;
@@ -1011,6 +1005,7 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
+ LOCK(node->cs_filter);
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
@@ -1232,11 +1227,11 @@ void CConnman::NotifyNumConnectionsChanged()
void CConnman::InactivityCheck(CNode *pnode)
{
int64_t nTime = GetSystemTimeInSeconds();
- if (nTime - pnode->nTimeConnected > 60)
+ if (nTime - pnode->nTimeConnected > m_peer_connect_timeout)
{
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
- LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
+ LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d from %d\n", m_peer_connect_timeout, pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
diff --git a/src/net.h b/src/net.h
index 164ec9080c..775d0c8099 100644
--- a/src/net.h
+++ b/src/net.h
@@ -78,6 +78,8 @@ static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24;
/** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false;
+/** -peertimeout default */
+static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
static const bool DEFAULT_FORCEDNSSEED = false;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
@@ -138,6 +140,7 @@ public:
unsigned int nReceiveFloodSize = 0;
uint64_t nMaxOutboundTimeframe = 0;
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;
@@ -158,6 +161,7 @@ public:
m_msgproc = connOptions.m_msgproc;
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
nReceiveFloodSize = connOptions.nReceiveFloodSize;
+ m_peer_connect_timeout = connOptions.m_peer_connect_timeout;
{
LOCK(cs_totalBytesSent);
nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
@@ -391,6 +395,9 @@ private:
uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent);
uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent);
+ // P2P timeout in seconds
+ int64_t m_peer_connect_timeout;
+
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
std::vector<CSubNet> vWhitelistedRange;
@@ -400,12 +407,12 @@ private:
std::vector<ListenSocket> vhListenSocket;
std::atomic<bool> fNetworkActive;
- banmap_t setBanned;
+ banmap_t setBanned GUARDED_BY(cs_setBanned);
CCriticalSection cs_setBanned;
- bool setBannedIsDirty;
+ bool setBannedIsDirty GUARDED_BY(cs_setBanned);
bool fAddressesInitialized;
CAddrMan addrman;
- std::deque<std::string> vOneShots;
+ std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots);
CCriticalSection cs_vOneShots;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
@@ -540,7 +547,7 @@ struct LocalServiceInfo {
};
extern CCriticalSection cs_mapLocalHost;
-extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
+extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
class CNodeStats
@@ -630,23 +637,23 @@ class CNode
public:
// socket
std::atomic<ServiceFlags> nServices;
- SOCKET hSocket;
+ SOCKET hSocket GUARDED_BY(cs_hSocket);
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
- uint64_t nSendBytes;
- std::deque<std::vector<unsigned char>> vSendMsg;
+ uint64_t nSendBytes GUARDED_BY(cs_vSend);
+ std::deque<std::vector<unsigned char>> vSendMsg GUARDED_BY(cs_vSend);
CCriticalSection cs_vSend;
CCriticalSection cs_hSocket;
CCriticalSection cs_vRecv;
CCriticalSection cs_vProcessMsg;
- std::list<CNetMessage> vProcessMsg;
+ std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg);
size_t nProcessQueueSize;
CCriticalSection cs_sendProcessing;
std::deque<CInv> vRecvGetData;
- uint64_t nRecvBytes;
+ uint64_t nRecvBytes GUARDED_BY(cs_vRecv);
std::atomic<int> nRecvVersion;
std::atomic<int64_t> nLastSend;
@@ -662,7 +669,7 @@ public:
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
- std::string strSubVer, cleanSubVer;
+ std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
@@ -677,11 +684,11 @@ public:
// a) it allows us to not relay tx invs before receiving the peer's version message
// b) the peer may tell us in its version message that we should not relay tx invs
// unless it loads a bloom filter.
- bool fRelayTxes; //protected by cs_filter
+ bool fRelayTxes GUARDED_BY(cs_filter);
bool fSentAddr;
CSemaphoreGrant grantOutbound;
- CCriticalSection cs_filter;
- std::unique_ptr<CBloomFilter> pfilter;
+ mutable CCriticalSection cs_filter;
+ std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
std::atomic<int> nRefCount;
const uint64_t nKeyedNetGroup;
@@ -690,7 +697,7 @@ public:
protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
- mapMsgCmdSize mapRecvBytesPerMsgCmd;
+ mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
public:
uint256 hashContinue;
@@ -701,27 +708,26 @@ public:
CRollingBloomFilter addrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
- int64_t nNextAddrSend;
- int64_t nNextLocalAddrSend;
+ int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing);
+ int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing);
// inventory based relay
- CRollingBloomFilter filterInventoryKnown;
+ CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
// Set of transaction ids we still have to announce.
// They are sorted by the mempool before relay, so the order is not important.
std::set<uint256> setInventoryTxToSend;
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
- std::vector<uint256> vInventoryBlockToSend;
+ std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
CCriticalSection cs_inventory;
std::set<uint256> setAskFor;
std::multimap<int64_t, CInv> mapAskFor;
int64_t nNextInvSend;
// Used for headers announcements - unfiltered blocks to relay
- // Also protected by cs_inventory
- std::vector<uint256> vBlockHashesToAnnounce;
- // Used for BIP35 mempool sending, also protected by cs_inventory
- bool fSendMempool;
+ std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
+ // Used for BIP35 mempool sending
+ bool fSendMempool GUARDED_BY(cs_inventory);
// Last time a "MEMPOOL" request was serviced.
std::atomic<int64_t> timeLastMempoolReq;
@@ -742,7 +748,7 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued;
// Minimum fee rate with which to filter inv's to this node
- CAmount minFeeFilter;
+ CAmount minFeeFilter GUARDED_BY(cs_feeFilter);
CCriticalSection cs_feeFilter;
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
@@ -762,10 +768,10 @@ private:
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
- std::string addrName;
+ std::string addrName GUARDED_BY(cs_addrName);
// Our address, as reported by the peer
- CService addrLocal;
+ CService addrLocal GUARDED_BY(cs_addrLocal);
mutable CCriticalSection cs_addrLocal;
public:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f4ab3aa153..40c5112578 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -566,7 +566,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
return;
}
if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
- if (pindex->nChainTx)
+ if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
// The block is not already downloaded, and not yet in flight.
@@ -1124,7 +1124,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(inv.hash);
if (pindex) {
- if (pindex->nChainTx && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
+ if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
pindex->IsValid(BLOCK_VALID_TREE)) {
// If we have the block and all of its parents, but have not yet validated it,
// we might be in the middle of connecting it (ie in the unlock of cs_main
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index e1af4eff62..a0c7f8e3c2 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -17,7 +17,6 @@ static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87,
CNetAddr::CNetAddr()
{
memset(ip, 0, sizeof(ip));
- scopeId = 0;
}
void CNetAddr::SetIP(const CNetAddr& ipIn)
@@ -83,6 +82,16 @@ unsigned int CNetAddr::GetByte(int n) const
return ip[15-n];
}
+bool CNetAddr::IsBindAny() const
+{
+ const int cmplen = IsIPv4() ? 4 : 16;
+ for (int i = 0; i < cmplen; ++i) {
+ if (GetByte(i)) return false;
+ }
+
+ return true;
+}
+
bool CNetAddr::IsIPv4() const
{
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
diff --git a/src/netaddress.h b/src/netaddress.h
index cc0e4d4f12..ca435d17dc 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -33,7 +33,7 @@ class CNetAddr
{
protected:
unsigned char ip[16]; // in network byte order
- uint32_t scopeId; // for scoped/link-local ipv6 addresses
+ uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
public:
CNetAddr();
@@ -55,6 +55,7 @@ class CNetAddr
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
+ bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 6a750d5141..1c043fc981 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -26,9 +26,9 @@
#endif
// Settings
-static proxyType proxyInfo[NET_MAX];
-static proxyType nameProxy;
static CCriticalSection cs_proxyInfos;
+static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos);
+static proxyType nameProxy GUARDED_BY(cs_proxyInfos);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
diff --git a/src/outputtype.h b/src/outputtype.h
index 4c4d93bc8b..6c30fd1950 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_OUTPUTTYPE_H
#define BITCOIN_OUTPUTTYPE_H
+#include <attributes.h>
#include <keystore.h>
#include <script/standard.h>
@@ -26,7 +27,7 @@ enum class OutputType {
CHANGE_AUTO,
};
-bool ParseOutputType(const std::string& str, OutputType& output_type);
+NODISCARD bool ParseOutputType(const std::string& str, OutputType& output_type);
const std::string& FormatOutputType(OutputType type);
/**
diff --git a/src/prevector.h b/src/prevector.h
index aa77573746..99e5751634 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -15,8 +15,6 @@
#include <iterator>
#include <type_traits>
-#include <compat.h>
-
#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
@@ -198,11 +196,7 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
- void fill(T* dst, ptrdiff_t count) {
- std::fill_n(dst, count, T{});
- }
-
- void fill(T* dst, ptrdiff_t count, const T& value) {
+ void fill(T* dst, ptrdiff_t count, const T& value = T{}) {
std::fill_n(dst, count, value);
}
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 0f834eb8c1..c88d5b1ad3 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -21,7 +21,9 @@ public:
uint256 hash;
uint32_t n;
- COutPoint(): n((uint32_t) -1) { }
+ static constexpr uint32_t NULL_INDEX = std::numeric_limits<uint32_t>::max();
+
+ COutPoint(): n(NULL_INDEX) { }
COutPoint(const uint256& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
ADD_SERIALIZE_METHODS;
@@ -32,8 +34,8 @@ public:
READWRITE(n);
}
- void SetNull() { hash.SetNull(); n = (uint32_t) -1; }
- bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); }
+ void SetNull() { hash.SetNull(); n = NULL_INDEX; }
+ bool IsNull() const { return (hash.IsNull() && n == NULL_INDEX); }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index a0270daf99..821c23c467 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -152,14 +152,15 @@ void AskPassphraseDialog::accept()
}
} break;
case Unlock:
- if(!model->setWalletLocked(false, oldpass))
- {
- QMessageBox::critical(this, tr("Wallet unlock failed"),
- tr("The passphrase entered for the wallet decryption was incorrect."));
- }
- else
- {
- QDialog::accept(); // Success
+ try {
+ if (!model->setWalletLocked(false, oldpass)) {
+ QMessageBox::critical(this, tr("Wallet unlock failed"),
+ tr("The passphrase entered for the wallet decryption was incorrect."));
+ } else {
+ QDialog::accept(); // Success
+ }
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
}
break;
case Decrypt:
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 7508f596e6..eaeb93a652 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -71,11 +71,6 @@ Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(uint256)
-static void InitMessage(const std::string& message)
-{
- noui_InitMessage(message);
-}
-
/** Translate string to current locale using Qt. */
const std::function<std::string(const char*)> G_TRANSLATION_FUN = [](const char* psz) {
return QCoreApplication::translate("bitcoin-core", psz).toStdString();
@@ -217,7 +212,7 @@ Q_SIGNALS:
void requestedInitialize();
void requestedShutdown();
void stopThread();
- void splashFinished(QWidget *window);
+ void splashFinished();
private:
QThread *coreThread;
@@ -357,9 +352,9 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
SplashScreen *splash = new SplashScreen(m_node, 0, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
- // screen will take care of deleting itself when slotFinish happens.
+ // screen will take care of deleting itself when finish() happens.
splash->show();
- connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::slotFinish);
+ connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
}
@@ -500,7 +495,7 @@ void BitcoinApplication::initializeResult(bool success)
{
window->show();
}
- Q_EMIT splashFinished(window);
+ Q_EMIT splashFinished();
#ifdef ENABLE_WALLET
// Now that initialization/startup is done, process any command-line
@@ -514,7 +509,7 @@ void BitcoinApplication::initializeResult(bool success)
#endif
pollShutdownTimer->start(200);
} else {
- Q_EMIT splashFinished(window); // Make sure splash screen doesn't stick around during shutdown
+ Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
quit(); // Exit first main loop invocation
}
}
@@ -563,6 +558,11 @@ int main(int argc, char *argv[])
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
+ // Subscribe to global signals from core
+ std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox);
+ std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion);
+ std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage);
+
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
/// 1. Basic Qt initialization (not dependent on parameters or configuration)
@@ -570,10 +570,8 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(bitcoin_locale);
BitcoinApplication app(*node, argc, argv);
-#if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#endif
#if QT_VERSION >= 0x050600
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
@@ -698,9 +696,6 @@ int main(int argc, char *argv[])
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
- // Subscribe to global signals from core
- std::unique_ptr<interfaces::Handler> handler = node->handleInitMessage(InitMessage);
-
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index b68f3a439b..558fcf50ba 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -23,9 +23,7 @@ class AmountSpinBox: public QAbstractSpinBox
public:
explicit AmountSpinBox(QWidget *parent):
- QAbstractSpinBox(parent),
- currentUnit(BitcoinUnits::BTC),
- singleStep(100000) // satoshis
+ QAbstractSpinBox(parent)
{
setAlignment(Qt::AlignRight);
@@ -44,10 +42,19 @@ public:
void fixup(QString &input) const
{
- bool valid = false;
- CAmount val = parse(input, &valid);
- if(valid)
- {
+ bool valid;
+ CAmount val;
+
+ if (input.isEmpty() && !m_allow_empty) {
+ valid = true;
+ val = m_min_amount;
+ } else {
+ valid = false;
+ val = parse(input, &valid);
+ }
+
+ if (valid) {
+ val = qBound(m_min_amount, val, m_max_amount);
input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
lineEdit()->setText(input);
}
@@ -64,12 +71,27 @@ public:
Q_EMIT valueChanged();
}
+ void SetAllowEmpty(bool allow)
+ {
+ m_allow_empty = allow;
+ }
+
+ void SetMinValue(const CAmount& value)
+ {
+ m_min_amount = value;
+ }
+
+ void SetMaxValue(const CAmount& value)
+ {
+ m_max_amount = value;
+ }
+
void stepBy(int steps)
{
bool valid = false;
CAmount val = value(&valid);
val = val + steps * singleStep;
- val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
+ val = qBound(m_min_amount, val, m_max_amount);
setValue(val);
}
@@ -125,9 +147,12 @@ public:
}
private:
- int currentUnit;
- CAmount singleStep;
+ int currentUnit{BitcoinUnits::BTC};
+ CAmount singleStep{CAmount(100000)}; // satoshis
mutable QSize cachedMinimumSizeHint;
+ bool m_allow_empty{true};
+ CAmount m_min_amount{CAmount(0)};
+ CAmount m_max_amount{BitcoinUnits::maxMoney()};
/**
* Parse a string into a number of base monetary units and
@@ -174,11 +199,10 @@ protected:
StepEnabled rv = 0;
bool valid = false;
CAmount val = value(&valid);
- if(valid)
- {
- if(val > 0)
+ if (valid) {
+ if (val > m_min_amount)
rv |= StepDownEnabled;
- if(val < BitcoinUnits::maxMoney())
+ if (val < m_max_amount)
rv |= StepUpEnabled;
}
return rv;
@@ -275,6 +299,21 @@ void BitcoinAmountField::setValue(const CAmount& value)
amount->setValue(value);
}
+void BitcoinAmountField::SetAllowEmpty(bool allow)
+{
+ amount->SetAllowEmpty(allow);
+}
+
+void BitcoinAmountField::SetMinValue(const CAmount& value)
+{
+ amount->SetMinValue(value);
+}
+
+void BitcoinAmountField::SetMaxValue(const CAmount& value)
+{
+ amount->SetMaxValue(value);
+}
+
void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
amount->setReadOnly(fReadOnly);
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index f93579c492..650481e30d 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -31,6 +31,15 @@ public:
CAmount value(bool *value=0) const;
void setValue(const CAmount& value);
+ /** If allow empty is set to false the field will be set to the minimum allowed value if left empty. **/
+ void SetAllowEmpty(bool allow);
+
+ /** Set the minimum value in satoshis **/
+ void SetMinValue(const CAmount& value);
+
+ /** Set the maximum value in satoshis **/
+ void SetMaxValue(const CAmount& value);
+
/** Set single step in satoshis **/
void setSingleStep(const CAmount& step);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ef82351551..a16b2ddebf 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -31,7 +31,6 @@
#include <chainparams.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
-#include <noui.h>
#include <ui_interface.h>
#include <util/system.h>
@@ -1089,10 +1088,10 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
return false;
}
-void BitcoinGUI::setHDStatus(int hdEnabled)
+void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
{
- labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
+ labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
// eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
@@ -1138,7 +1137,7 @@ void BitcoinGUI::updateWalletStatus()
}
WalletModel * const walletModel = walletView->getWalletModel();
setEncryptionStatus(walletModel->getEncryptionStatus());
- setHDStatus(walletModel->wallet().hdEnabled());
+ setHDStatus(walletModel->privateKeysDisabled(), walletModel->wallet().hdEnabled());
}
#endif // ENABLE_WALLET
@@ -1226,9 +1225,6 @@ void BitcoinGUI::showModalOverlay()
static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, const std::string& caption, unsigned int style)
{
- // Redundantly log and print message in non-gui fashion
- noui_ThreadSafeMessageBox(message, caption, style);
-
bool modal = (style & CClientUIInterface::MODAL);
// The SECURE flag has no effect in the Qt GUI.
// bool secure = (style & CClientUIInterface::SECURE);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index e8b857c17c..aeff5dae30 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -223,7 +223,7 @@ private:
@param[in] hdEnabled current hd enabled status
@see WalletModel::EncryptionStatus
*/
- void setHDStatus(int hdEnabled);
+ void setHDStatus(bool privkeyDisabled, int hdEnabled);
public Q_SLOTS:
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index ea970c0bc9..77f8bcf901 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -129,8 +129,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
- ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it
- ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
@@ -203,10 +201,10 @@ void CoinControlDialog::showMenu(const QPoint &point)
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
- if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
+ if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
- if (model->wallet().isLockedCoin(COutPoint(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())))
+ if (model->wallet().isLockedCoin(COutPoint(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
@@ -256,7 +254,7 @@ void CoinControlDialog::copyAddress()
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash()
{
- GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH));
+ GUIUtil::setClipboard(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString());
}
// context menu action: lock coin
@@ -265,7 +263,7 @@ void CoinControlDialog::lockCoin()
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
- COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
@@ -275,7 +273,7 @@ void CoinControlDialog::lockCoin()
// context menu action: unlock coin
void CoinControlDialog::unlockCoin()
{
- COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
@@ -371,9 +369,9 @@ void CoinControlDialog::radioListMode(bool checked)
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
{
- if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
+ if (column == COLUMN_CHECKBOX && item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
- COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
coinControl()->UnSelect(outpt);
@@ -693,10 +691,10 @@ void CoinControlDialog::updateView()
itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.depth_in_main_chain));
// transaction hash
- itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(output.hash.GetHex()));
+ itemOutput->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(output.hash.GetHex()));
// vout index
- itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(output.n));
+ itemOutput->setData(COLUMN_ADDRESS, VOutRole, output.n);
// disable locked coins
if (model->wallet().isLockedCoin(output))
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 9c3f6a46a2..8f15ae4b20 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -80,9 +80,14 @@ private:
COLUMN_ADDRESS,
COLUMN_DATE,
COLUMN_CONFIRMATIONS,
- COLUMN_TXHASH,
- COLUMN_VOUT_INDEX,
};
+
+ enum
+ {
+ TxHashRole = Qt::UserRole,
+ VOutRole
+ };
+
friend class CCoinControlWidgetItem;
private Q_SLOTS:
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index d1237ad283..bd7f3c5f56 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -402,7 +402,7 @@
<bool>false</bool>
</property>
<property name="columnCount">
- <number>10</number>
+ <number>6</number>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 6b31ddea90..386d559281 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -878,28 +878,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee8">
<item>
- <widget class="QCheckBox" name="checkBoxMinimumFee">
- <property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="labelMinFeeWarning">
+ <widget class="QLabel" name="labelCustomFeeWarning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
+ <string>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
</property>
<property name="text">
- <string>(read the tooltip)</string>
- </property>
- <property name="margin">
- <number>5</number>
+ <string>A too low fee might result in a never confirming transaction (read the tooltip)</string>
</property>
</widget>
</item>
@@ -992,9 +979,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string/>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1009,9 +993,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1038,24 +1019,8 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>Confirmation time target:</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
- <item>
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>1</width>
- <height>1</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index b1cd2f77d0..2fc166b0c5 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -47,6 +47,7 @@
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
+#include <QFontDatabase>
#include <QKeyEvent>
#include <QLineEdit>
#include <QSettings>
@@ -55,11 +56,6 @@
#include <QUrlQuery>
#include <QMouseEvent>
-
-#if QT_VERSION >= 0x50200
-#include <QFontDatabase>
-#endif
-
#if defined(Q_OS_MAC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -82,13 +78,7 @@ QString dateTimeStr(qint64 nTime)
QFont fixedPitchFont()
{
-#if QT_VERSION >= 0x50200
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
-#else
- QFont font("Monospace");
- font.setStyleHint(QFont::Monospace);
- return font;
-#endif
}
// Just some dummy data to generate a convincing random-looking (but consistent) address
@@ -349,7 +339,7 @@ bool checkPoint(const QPoint &p, const QWidget *w)
{
QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
if (!atW) return false;
- return atW->topLevelWidget() == w;
+ return atW->window() == w;
}
bool isObscured(QWidget *w)
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 1db9609979..bec79335e7 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -161,15 +161,21 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
{
int unit = walletModel->getOptionsModel()->getDisplayUnit();
m_balances = balances;
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
-
+ if (walletModel->privateKeysDisabled()) {
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ } else {
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ }
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
bool showImmature = balances.immature_balance != 0;
@@ -178,7 +184,7 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
// for symmetry reasons also show immature label when the watch-only one is shown
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature);
- ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance
+ ui->labelWatchImmature->setVisible(!walletModel->privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance
}
// show/hide watch-only labels
@@ -231,8 +237,10 @@ void OverviewPage::setWalletModel(WalletModel *model)
connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit);
- updateWatchOnlyLabels(wallet.haveWatchOnly());
- connect(model, &WalletModel::notifyWatchonlyChanged, this, &OverviewPage::updateWatchOnlyLabels);
+ updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->privateKeysDisabled());
+ connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) {
+ updateWatchOnlyLabels(showWatchOnly && !walletModel->privateKeysDisabled());
+ });
}
// update the display unit, to not use the default ("BTC")
diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/movies/makespinner.sh
index 76e36e4f31..f47c66e02c 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/movies/makespinner.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+#
# Copyright (c) 2014-2015 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 858128f9f9..65db0280b7 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -119,13 +119,11 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
- if (!settings.contains("fPayOnlyMinFee"))
- settings.setValue("fPayOnlyMinFee", false);
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
+ ui->customFee->SetAllowEmpty(false);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
- ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
@@ -174,14 +172,15 @@ void SendCoinsDialog::setModel(WalletModel *_model)
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::setMinimumFee);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateFeeSectionControls);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- ui->customFee->setSingleStep(model->wallet().getRequiredFee(1000));
+ CAmount requiredFee = model->wallet().getRequiredFee(1000);
+ ui->customFee->SetMinValue(requiredFee);
+ if (ui->customFee->value() < requiredFee) {
+ ui->customFee->setValue(requiredFee);
+ }
+ ui->customFee->setSingleStep(requiredFee);
updateFeeSectionControls();
- updateMinFeeLabel();
updateSmartFeeLabel();
// set default rbf checkbox state
@@ -210,7 +209,6 @@ SendCoinsDialog::~SendCoinsDialog()
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
- settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
delete ui;
}
@@ -542,7 +540,6 @@ void SendCoinsDialog::updateDisplayUnit()
{
setBalance(model->wallet().getBalances());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
- updateMinFeeLabel();
updateSmartFeeLabel();
}
@@ -642,11 +639,6 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
}
}
-void SendCoinsDialog::setMinimumFee()
-{
- ui->customFee->setValue(model->wallet().getRequiredFee(1000));
-}
-
void SendCoinsDialog::updateFeeSectionControls()
{
ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
@@ -654,10 +646,9 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
- ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
- ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+ ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
}
void SendCoinsDialog::updateFeeMinimizedLabel()
@@ -672,14 +663,6 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
}
}
-void SendCoinsDialog::updateMinFeeLabel()
-{
- if (model && model->getOptionsModel())
- ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
- BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getRequiredFee(1000)) + "/kB")
- );
-}
-
void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
{
if (ui->radioCustomFee->isChecked()) {
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 7009855f17..e1ebc77d59 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -92,9 +92,7 @@ private Q_SLOTS:
void coinControlClipboardBytes();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
- void setMinimumFee();
void updateFeeSectionControls();
- void updateMinFeeLabel();
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index b6235e91f7..7b952f9fd7 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -37,9 +37,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
float fontFactor = 1.0;
float devicePixelRatio = 1.0;
-#if QT_VERSION > 0x050100
devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();
-#endif
// define text to place
QString titleText = tr(PACKAGE_NAME);
@@ -53,10 +51,8 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
QSize splashSize(480*devicePixelRatio,320*devicePixelRatio);
pixmap = QPixmap(splashSize);
-#if QT_VERSION > 0x050100
// change to HiDPI if it makes sense
pixmap.setDevicePixelRatio(devicePixelRatio);
-#endif
QPainter pixPaint(&pixmap);
pixPaint.setPen(QColor(100,100,100));
@@ -149,10 +145,8 @@ bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
return QObject::eventFilter(obj, ev);
}
-void SplashScreen::slotFinish(QWidget *mainWin)
+void SplashScreen::finish()
{
- Q_UNUSED(mainWin);
-
/* If the window is minimized, hide() will be ignored. */
/* Make sure we de-minimize the splashscreen window before hiding */
if (isMinimized())
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index c28b6e5660..f99dd0c701 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -5,8 +5,7 @@
#ifndef BITCOIN_QT_SPLASHSCREEN_H
#define BITCOIN_QT_SPLASHSCREEN_H
-#include <functional>
-#include <QSplashScreen>
+#include <QWidget>
#include <memory>
@@ -37,8 +36,8 @@ protected:
void closeEvent(QCloseEvent *event);
public Q_SLOTS:
- /** Slot to call finish() method as it's not defined as slot */
- void slotFinish(QWidget *mainWin);
+ /** Hide the splash screen window and schedule the splash screen object for deletion */
+ void finish();
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
diff --git a/src/qt/test/util.h b/src/qt/test/util.h
index 5363c94547..377f07dcba 100644
--- a/src/qt/test/util.h
+++ b/src/qt/test/util.h
@@ -1,6 +1,8 @@
#ifndef BITCOIN_QT_TEST_UTIL_H
#define BITCOIN_QT_TEST_UTIL_H
+#include <QString>
+
/**
* Press "Ok" button in message box dialog.
*
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index ebf7bad795..b051dd159b 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -23,6 +23,7 @@
#include <init.h>
#include <interfaces/node.h>
#include <util/system.h>
+#include <util/strencodings.h>
#include <stdio.h>
@@ -54,9 +55,9 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
{
setWindowTitle(tr("About %1").arg(tr(PACKAGE_NAME)));
+ std::string licenseInfo = LicenseInfo();
/// HTML-format the license message from the core
- QString licenseInfo = QString::fromStdString(LicenseInfo());
- QString licenseInfoHTML = licenseInfo;
+ QString licenseInfoHTML = QString::fromStdString(LicenseInfo());
// Make URLs clickable
QRegExp uri("<(.*)>", Qt::CaseSensitive, QRegExp::RegExp2);
uri.setMinimal(true); // use non-greedy matching
@@ -66,7 +67,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
ui->aboutMessage->setTextFormat(Qt::RichText);
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- text = version + "\n" + licenseInfo;
+ text = version + "\n" + QString::fromStdString(FormatParagraph(licenseInfo));
ui->aboutMessage->setText(version + "<br><br>" + licenseInfoHTML);
ui->aboutMessage->setWordWrap(true);
ui->helpMessage->setVisible(false);
diff --git a/src/rest.cpp b/src/rest.cpp
index 6c7e0384cb..4988e6ed26 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -3,20 +3,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <attributes.h>
#include <chain.h>
#include <chainparams.h>
#include <core_io.h>
+#include <httpserver.h>
#include <index/txindex.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <validation.h>
-#include <httpserver.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <streams.h>
#include <sync.h>
#include <txmempool.h>
#include <util/strencodings.h>
+#include <validation.h>
#include <version.h>
#include <boost/algorithm/string.hpp>
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 92f6f0fe11..ec87f42c93 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -12,8 +12,8 @@
#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
-#include <validation.h>
#include <core_io.h>
+#include <hash.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -21,14 +21,15 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <script/descriptor.h>
#include <streams.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h>
-#include <util/system.h>
#include <util/strencodings.h>
-#include <hash.h>
+#include <util/system.h>
+#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
#include <warnings.h>
@@ -38,7 +39,6 @@
#include <univalue.h>
-#include <boost/algorithm/string.hpp>
#include <boost/thread/thread.hpp> // boost::thread::interrupt
#include <memory>
@@ -162,8 +162,9 @@ static UniValue getblockcount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getblockcount\n"
- "\nReturns the number of blocks in the longest blockchain.\n"
+ RPCHelpMan{"getblockcount",
+ "\nReturns the number of blocks in the longest blockchain.\n", {}}
+ .ToString() +
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n"
@@ -179,8 +180,9 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getbestblockhash\n"
- "\nReturns the hash of the best (tip) block in the longest blockchain.\n"
+ RPCHelpMan{"getbestblockhash",
+ "\nReturns the hash of the best (tip) block in the longest blockchain.\n", {}}
+ .ToString() +
"\nResult:\n"
"\"hex\" (string) the block hash, hex-encoded\n"
"\nExamples:\n"
@@ -206,11 +208,13 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "waitfornewblock (timeout)\n"
- "\nWaits for a specific new block and returns useful info about it.\n"
- "\nReturns the current block on timeout or exit.\n"
- "\nArguments:\n"
- "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ RPCHelpMan{"waitfornewblock",
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -244,12 +248,14 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "waitforblock <blockhash> (timeout)\n"
- "\nWaits for a specific new block and returns useful info about it.\n"
- "\nReturns the current block on timeout or exit.\n"
- "\nArguments:\n"
- "1. \"blockhash\" (required, string) Block hash to wait for.\n"
- "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ RPCHelpMan{"waitforblock",
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "Block hash to wait for."},
+ {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -286,13 +292,15 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "waitforblockheight <height> (timeout)\n"
- "\nWaits for (at least) block height and returns the height and hash\n"
- "of the current tip.\n"
- "\nReturns the current block on timeout or exit.\n"
- "\nArguments:\n"
- "1. height (required, int) Block height to wait for (int)\n"
- "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ RPCHelpMan{"waitforblockheight",
+ "\nWaits for (at least) block height and returns the height and hash\n"
+ "of the current tip.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Block height to wait for."},
+ {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -328,8 +336,9 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
- "syncwithvalidationinterfacequeue\n"
- "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n"
+ RPCHelpMan{"syncwithvalidationinterfacequeue",
+ "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("syncwithvalidationinterfacequeue","")
+ HelpExampleRpc("syncwithvalidationinterfacequeue","")
@@ -343,8 +352,9 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getdifficulty\n"
- "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
+ RPCHelpMan{"getdifficulty",
+ "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", {}}
+ .ToString() +
"\nResult:\n"
"n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
"\nExamples:\n"
@@ -477,11 +487,13 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getrawmempool ( verbose )\n"
- "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
- "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n"
- "\nArguments:\n"
- "1. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
+ RPCHelpMan{"getrawmempool",
+ "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
+ "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
+ {
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ }}
+ .ToString() +
"\nResult: (for verbose = false):\n"
"[ (json array of string)\n"
" \"transactionid\" (string) The transaction id\n"
@@ -509,11 +521,13 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempoolancestors txid ( verbose )\n"
- "\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
- "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
+ RPCHelpMan{"getmempoolancestors",
+ "\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ }}
+ .ToString() +
"\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
@@ -573,11 +587,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempooldescendants txid ( verbose )\n"
- "\nIf txid is in the mempool, returns all in-mempool descendants.\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
- "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
+ RPCHelpMan{"getmempooldescendants",
+ "\nIf txid is in the mempool, returns all in-mempool descendants.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ }}
+ .ToString() +
"\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
@@ -637,10 +653,12 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "getmempoolentry txid\n"
- "\nReturns mempool data for given transaction\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
+ RPCHelpMan{"getmempoolentry",
+ "\nReturns mempool data for given transaction\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
+ EntryDescriptionString()
@@ -670,10 +688,12 @@ static UniValue getblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "getblockhash height\n"
- "\nReturns hash of block in best-block-chain at height provided.\n"
- "\nArguments:\n"
- "1. height (numeric, required) The height index\n"
+ RPCHelpMan{"getblockhash",
+ "\nReturns hash of block in best-block-chain at height provided.\n",
+ {
+ {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The height index"},
+ }}
+ .ToString() +
"\nResult:\n"
"\"hash\" (string) The block hash\n"
"\nExamples:\n"
@@ -695,12 +715,14 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getblockheader \"hash\" ( verbose )\n"
- "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
- "If verbose is true, returns an Object with information about blockheader <hash>.\n"
- "\nArguments:\n"
- "1. \"hash\" (string, required) The block hash\n"
- "2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n"
+ RPCHelpMan{"getblockheader",
+ "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
+ "If verbose is true, returns an Object with information about blockheader <hash>.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"},
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "true for a json object, false for the hex-encoded data"},
+ }}
+ .ToString() +
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
@@ -773,13 +795,15 @@ static UniValue getblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getblock \"blockhash\" ( verbosity ) \n"
- "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
- "If verbosity is 1, returns an Object with information about block <hash>.\n"
- "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n"
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) The block hash\n"
- "2. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data\n"
+ RPCHelpMan{"getblock",
+ "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
+ "If verbosity is 1, returns an Object with information about block <hash>.\n"
+ "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"},
+ {"verbosity", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
+ }}
+ .ToString() +
"\nResult (for verbosity = 0):\n"
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
"\nResult (for verbosity = 1):\n"
@@ -925,10 +949,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "pruneblockchain\n"
- "\nArguments:\n"
- "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
- " to prune blocks whose block time is at least 2 hours older than the provided timestamp.\n"
+ RPCHelpMan{"pruneblockchain", "",
+ {
+ {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
+ " to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
+ }}
+ .ToString() +
"\nResult:\n"
"n (numeric) Height of the last block pruned.\n"
"\nExamples:\n"
@@ -974,9 +1000,11 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "gettxoutsetinfo\n"
- "\nReturns statistics about the unspent transaction output set.\n"
- "Note this call may take some time.\n"
+ RPCHelpMan{"gettxoutsetinfo",
+ "\nReturns statistics about the unspent transaction output set.\n"
+ "Note this call may take some time.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
@@ -1016,13 +1044,14 @@ UniValue gettxout(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
- "gettxout \"txid\" n ( include_mempool )\n"
- "\nReturns details about an unspent transaction output.\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
- "2. \"n\" (numeric, required) vout number\n"
- "3. \"include_mempool\" (boolean, optional) Whether to include the mempool. Default: true."
- " Note that an unspent output that is spent in the mempool won't appear.\n"
+ RPCHelpMan{"gettxout",
+ "\nReturns details about an unspent transaction output.\n",
+ {
+ {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"n", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "vout number"},
+ {"include_mempool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n"
@@ -1096,11 +1125,13 @@ static UniValue verifychain(const JSONRPCRequest& request)
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "verifychain ( checklevel nblocks )\n"
- "\nVerifies blockchain database.\n"
- "\nArguments:\n"
- "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n"
- "2. nblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n"
+ RPCHelpMan{"verifychain",
+ "\nVerifies blockchain database.\n",
+ {
+ {"checklevel", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, range=0-4", nCheckLevel), "How thorough the block verification is."},
+ {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."},
+ }}
+ .ToString() +
"\nResult:\n"
"true|false (boolean) Verified or not\n"
"\nExamples:\n"
@@ -1193,8 +1224,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getblockchaininfo\n"
- "Returns an object containing various state info regarding blockchain processing.\n"
+ RPCHelpMan{"getblockchaininfo",
+ "Returns an object containing various state info regarding blockchain processing.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
@@ -1310,9 +1342,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getchaintips\n"
- "Return information about all known tips in the block tree,"
- " including the main chain as well as orphaned branches.\n"
+ RPCHelpMan{"getchaintips",
+ "Return information about all known tips in the block tree,"
+ " including the main chain as well as orphaned branches.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -1388,7 +1422,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
} else if (block->nStatus & BLOCK_FAILED_MASK) {
// This block or one of its ancestors is invalid.
status = "invalid";
- } else if (block->nChainTx == 0) {
+ } else if (!block->HaveTxsDownloaded()) {
// This block cannot be connected because full block data for it or one of its parents is missing.
status = "headers-only";
} else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
@@ -1427,8 +1461,9 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getmempoolinfo\n"
- "\nReturns details on the active state of the TX memory pool.\n"
+ RPCHelpMan{"getmempoolinfo",
+ "\nReturns details on the active state of the TX memory pool.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"size\": xxxxx, (numeric) Current tx count\n"
@@ -1450,12 +1485,14 @@ static UniValue preciousblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "preciousblock \"blockhash\"\n"
- "\nTreats a block as if it were received before others with the same work.\n"
- "\nA later preciousblock call can override the effect of an earlier one.\n"
- "\nThe effects of preciousblock are not retained across restarts.\n"
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to mark as precious\n"
+ RPCHelpMan{"preciousblock",
+ "\nTreats a block as if it were received before others with the same work.\n"
+ "\nA later preciousblock call can override the effect of an earlier one.\n"
+ "\nThe effects of preciousblock are not retained across restarts.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as precious"},
+ }}
+ .ToString() +
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("preciousblock", "\"blockhash\"")
@@ -1487,10 +1524,12 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "invalidateblock \"blockhash\"\n"
- "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to mark as invalid\n"
+ RPCHelpMan{"invalidateblock",
+ "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as invalid"},
+ }}
+ .ToString() +
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("invalidateblock", "\"blockhash\"")
@@ -1525,11 +1564,13 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "reconsiderblock \"blockhash\"\n"
- "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
- "This can be used to undo the effects of invalidateblock.\n"
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to reconsider\n"
+ RPCHelpMan{"reconsiderblock",
+ "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
+ "This can be used to undo the effects of invalidateblock.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to reconsider"},
+ }}
+ .ToString() +
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("reconsiderblock", "\"blockhash\"")
@@ -1562,11 +1603,13 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getchaintxstats ( nblocks blockhash )\n"
- "\nCompute statistics about the total number and rate of transactions in the chain.\n"
- "\nArguments:\n"
- "1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n"
- "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n"
+ RPCHelpMan{"getchaintxstats",
+ "\nCompute statistics about the total number and rate of transactions in the chain.\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "one month", "Size of the window in number of blocks"},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "chain tip", "The hash of the block that ends the window."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n"
@@ -1692,18 +1735,20 @@ static UniValue getblockstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) {
throw std::runtime_error(
- "getblockstats hash_or_height ( stats )\n"
- "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
- "It won't work for some heights with pruning.\n"
- "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n"
- "\nArguments:\n"
- "1. \"hash_or_height\" (string or numeric, required) The block hash or height of the target block\n"
- "2. \"stats\" (array, optional) Values to plot, by default all values (see result below)\n"
- " [\n"
- " \"height\", (string, optional) Selected statistic\n"
- " \"time\", (string, optional) Selected statistic\n"
- " ,...\n"
- " ]\n"
+ RPCHelpMan{"getblockstats",
+ "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
+ "It won't work for some heights with pruning.\n"
+ "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
+ {
+ {"hash_or_height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block hash or height of the target block", "", {"", "string or numeric"}},
+ {"stats", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "all values", "Values to plot (see result below)",
+ {
+ {"height", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
+ {"time", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
+ },
+ "stats"},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
" \"avgfee\": xxxxx, (numeric) Average fee in the block\n"
@@ -1956,8 +2001,9 @@ static UniValue savemempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "savemempool\n"
- "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n"
+ RPCHelpMan{"savemempool",
+ "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
@@ -2039,34 +2085,39 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "scantxoutset <action> ( <scanobjects> )\n"
- "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
- "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
- "Examples of output descriptors are:\n"
- " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
- " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
- " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
- " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
- "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
- "unhardened or hardened child keys.\n"
- "In the latter case, a range needs to be specified by below if different from 1000.\n"
- "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"
- "\nArguments:\n"
- "1. \"action\" (string, required) The action to execute\n"
+ RPCHelpMan{"scantxoutset",
+ "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
+ "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
+ "Examples of output descriptors are:\n"
+ " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
+ "unhardened or hardened child keys.\n"
+ "In the latter case, a range needs to be specified by below if different from 1000.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ {
+ {"action", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The action to execute\n"
" \"start\" for starting a scan\n"
" \"abort\" for aborting the current scan (returns true when abort was successful)\n"
- " \"status\" for progress report (in %) of the current scan\n"
- "2. \"scanobjects\" (array, required) Array of scan objects\n"
- " [ Every scan object is either a string descriptor or an object:\n"
- " \"descriptor\", (string, optional) An output descriptor\n"
- " { (object, optional) An object with output descriptor and metadata\n"
- " \"desc\": \"descriptor\", (string, required) An output descriptor\n"
- " \"range\": n, (numeric, optional) Up to what child index HD chains should be explored (default: 1000)\n"
- " },\n"
- " ...\n"
- " ]\n"
+ " \"status\" for progress report (in %) of the current scan"},
+ {"scanobjects", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Array of scan objects\n"
+ " Every scan object is either a string descriptor or an object:",
+ {
+ {"descriptor", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "An output descriptor"},
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "An object with output descriptor and metadata",
+ {
+ {"desc", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "An output descriptor"},
+ {"range", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000", "Up to what child index HD chains should be explored"},
+ },
+ },
+ },
+ "[scanobjects,...]"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"unspents\": [\n"
@@ -2074,6 +2125,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
" \"txid\" : \"transactionid\", (string) The transaction id\n"
" \"vout\": n, (numeric) the vout value\n"
" \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
" \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
" \"height\" : n, (numeric) Height of the unspent transaction output\n"
" }\n"
@@ -2108,6 +2160,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
}
std::set<CScript> needles;
+ std::map<CScript, std::string> descriptors;
CAmount total_in = 0;
// loop through the scan objects
@@ -2140,7 +2193,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
if (!desc->Expand(i, provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
}
- needles.insert(scripts.begin(), scripts.end());
+ for (const auto& script : scripts) {
+ std::string inferred = InferDescriptor(script, provider)->ToString();
+ needles.emplace(script);
+ descriptors.emplace(std::move(script), std::move(inferred));
+ }
}
}
@@ -2173,6 +2230,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
unspent.pushKV("txid", outpoint.hash.GetHex());
unspent.pushKV("vout", (int32_t)outpoint.n);
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
unspent.pushKV("height", (int32_t)coin.nHeight);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 2b99808c07..6f1bfb03d1 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -162,6 +162,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"},
{ "getnodeaddresses", 0, "count"},
+ { "stop", 0, "wait" },
};
// clang-format on
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 58b54c66de..93fa3a2728 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -18,10 +18,11 @@
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <txmempool.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -86,13 +87,15 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getnetworkhashps ( nblocks height )\n"
- "\nReturns the estimated network hashes per second based on the last n blocks.\n"
- "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
- "Pass in [height] to estimate the network speed at the time when a certain block was found.\n"
- "\nArguments:\n"
- "1. nblocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n"
- "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n"
+ RPCHelpMan{"getnetworkhashps",
+ "\nReturns the estimated network hashes per second based on the last n blocks.\n"
+ "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
+ "Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "120", "The number of blocks, or -1 for blocks since last difficulty change."},
+ {"height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "-1", "To estimate at the time of the given height."},
+ }}
+ .ToString() +
"\nResult:\n"
"x (numeric) Hashes per second estimated\n"
"\nExamples:\n"
@@ -156,12 +159,14 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
- "generatetoaddress nblocks address (maxtries)\n"
- "\nMine blocks immediately to a specified address (before the RPC call returns)\n"
- "\nArguments:\n"
- "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
- "2. address (string, required) The address to send the newly generated bitcoin to.\n"
- "3. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
+ RPCHelpMan{"generatetoaddress",
+ "\nMine blocks immediately to a specified address (before the RPC call returns)\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."},
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The address to send the newly generated bitcoin to."},
+ {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."},
+ }}
+ .ToString() +
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
@@ -192,8 +197,9 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getmininginfo\n"
- "\nReturns a json object containing mining-related information."
+ RPCHelpMan{"getmininginfo",
+ "\nReturns a json object containing mining-related information.", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
@@ -231,16 +237,18 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "prioritisetransaction <txid> <dummy value> <fee delta>\n"
- "Accepts the transaction into mined blocks at a higher (or lower) priority\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id.\n"
- "2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null.\n"
- " DEPRECATED. For forward compatibility use named arguments and omit this parameter.\n"
- "3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
+ RPCHelpMan{"prioritisetransaction",
+ "Accepts the transaction into mined blocks at a higher (or lower) priority\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id."},
+ {"dummy", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "API-Compatibility for previous API. Must be zero or null.\n"
+ " DEPRECATED. For forward compatibility use named arguments and omit this parameter."},
+ {"fee_delta", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The fee value (in satoshis) to add (or subtract, if negative).\n"
" Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
- " considers the transaction as it would have paid a higher (or lower) fee.\n"
+ " considers the transaction as it would have paid a higher (or lower) fee."},
+ }}
+ .ToString() +
"\nResult:\n"
"true (boolean) Returns true\n"
"\nExamples:\n"
@@ -294,30 +302,32 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getblocktemplate ( TemplateRequest )\n"
- "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
- "It returns data needed to construct a block to work on.\n"
- "For full specification, see BIPs 22, 23, 9, and 145:\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
-
- "\nArguments:\n"
- "1. template_request (json object, optional) A json object in the following spec\n"
- " {\n"
- " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), or omitted\n"
- " \"capabilities\":[ (array, optional) A list of strings\n"
- " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
- " ,...\n"
- " ],\n"
- " \"rules\":[ (array, optional) A list of strings\n"
- " \"support\" (string) client side supported softfork deployment\n"
- " ,...\n"
- " ]\n"
- " }\n"
- "\n"
-
+ RPCHelpMan{"getblocktemplate",
+ "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
+ "It returns data needed to construct a block to work on.\n"
+ "For full specification, see BIPs 22, 23, 9, and 145:\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ {
+ {"template_request", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "A json object in the following spec",
+ {
+ {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
+ {"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
+ {
+ {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ },
+ },
+ {"rules", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
+ {
+ {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"},
+ },
+ },
+ },
+ "\"template_request\""},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The preferred block version\n"
@@ -702,13 +712,14 @@ static UniValue submitblock(const JSONRPCRequest& request)
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "submitblock \"hexdata\" ( \"dummy\" )\n"
- "\nAttempts to submit new block to network.\n"
- "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
-
- "\nArguments\n"
- "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
- "2. \"dummy\" (optional) dummy value, for compatibility with BIP22. This value is ignored.\n"
+ RPCHelpMan{"submitblock",
+ "\nAttempts to submit new block to network.\n"
+ "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block data to submit"},
+ {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
+ }}
+ .ToString() +
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("submitblock", "\"mydata\"")
@@ -766,11 +777,13 @@ static UniValue submitheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "submitheader \"hexdata\"\n"
- "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
- "\nThrows when the header is invalid.\n"
- "\nArguments\n"
- "1. \"hexdata\" (string, required) the hex-encoded block header data\n"
+ RPCHelpMan{"submitheader",
+ "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
+ "\nThrows when the header is invalid.\n",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block header data"},
+ }}
+ .ToString() +
"\nResult:\n"
"None"
"\nExamples:\n" +
@@ -802,14 +815,14 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimatesmartfee conf_target (\"estimate_mode\")\n"
- "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within conf_target blocks if possible and return the number of blocks\n"
- "for which the estimate is valid. Uses virtual transaction size as defined\n"
- "in BIP 141 (witness data is discounted).\n"
- "\nArguments:\n"
- "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
- "2. \"estimate_mode\" (string, optional, default=CONSERVATIVE) The fee estimate mode.\n"
+ RPCHelpMan{"estimatesmartfee",
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within conf_target blocks if possible and return the number of blocks\n"
+ "for which the estimate is valid. Uses virtual transaction size as defined\n"
+ "in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "CONSERVATIVE", "The fee estimate mode.\n"
" Whether to return a more conservative estimate which also satisfies\n"
" a longer history. A conservative estimate potentially returns a\n"
" higher feerate and is more likely to be sufficient for the desired\n"
@@ -817,7 +830,9 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
" prevailing fee market. Must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
+ " \"CONSERVATIVE\""},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n"
@@ -863,19 +878,21 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimaterawfee conf_target (threshold)\n"
- "\nWARNING: This interface is unstable and may disappear or change!\n"
- "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
- " implementation of fee estimation. The parameters it can be called with\n"
- " and the results it returns will change if the internal implementation changes.\n"
- "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
- "defined in BIP 141 (witness data is discounted).\n"
- "\nArguments:\n"
- "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
- "2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n"
+ RPCHelpMan{"estimaterawfee",
+ "\nWARNING: This interface is unstable and may disappear or change!\n"
+ "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
+ " implementation of fee estimation. The parameters it can be called with\n"
+ " and the results it returns will change if the internal implementation changes.\n"
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
+ "defined in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"},
+ {"threshold", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0.95", "The proportion of transactions in a given feerate range that must have been\n"
" confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
- " lower buckets. Default: 0.95\n"
+ " lower buckets."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"short\" : { (json object, optional) estimate for short time horizon\n"
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index cebf12bb34..18c867bff3 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -32,14 +32,16 @@ static UniValue validateaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "validateaddress \"address\"\n"
- "\nReturn information about the given bitcoin address.\n"
- "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
- "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
- "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
- "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to validate\n"
+ RPCHelpMan{"validateaddress",
+ "\nReturn information about the given bitcoin address.\n"
+ "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
+ "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
+ "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
+ "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
@@ -78,18 +80,19 @@ static UniValue createmultisig(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
- std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\n"
- "\nCreates a multi-signature address with n signature of m keys required.\n"
- "It returns a json object with the address and redeemScript.\n"
- "\nArguments:\n"
- "1. nrequired (numeric, required) The number of required signatures out of the n keys.\n"
- "2. \"keys\" (string, required) A json array of hex-encoded public keys\n"
- " [\n"
- " \"key\" (string) The hex-encoded public key\n"
- " ,...\n"
- " ]\n"
- "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
-
+ std::string msg =
+ RPCHelpMan{"createmultisig",
+ "\nCreates a multi-signature address with n signature of m keys required.\n"
+ "It returns a json object with the address and redeemScript.\n",
+ {
+ {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys."},
+ {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex-encoded public keys.",
+ {
+ {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
+ }},
+ {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
@@ -142,12 +145,14 @@ static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "verifymessage \"address\" \"signature\" \"message\"\n"
- "\nVerify a signed message\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to use for the signature.\n"
- "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
- "3. \"message\" (string, required) The message that was signed.\n"
+ RPCHelpMan{"verifymessage",
+ "\nVerify a signed message\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."},
+ {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding (see signmessage)."},
+ {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."},
+ }}
+ .ToString() +
"\nResult:\n"
"true|false (boolean) If the signature is verified or not.\n"
"\nExamples:\n"
@@ -198,11 +203,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "signmessagewithprivkey \"privkey\" \"message\"\n"
- "\nSign a message with the private key of an address\n"
- "\nArguments:\n"
- "1. \"privkey\" (string, required) The private key to sign the message with.\n"
- "2. \"message\" (string, required) The message to create a signature of.\n"
+ RPCHelpMan{"signmessagewithprivkey",
+ "\nSign a message with the private key of an address\n",
+ {
+ {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."},
+ {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."},
+ }}
+ .ToString() +
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
"\nExamples:\n"
@@ -237,11 +244,13 @@ static UniValue setmocktime(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "setmocktime timestamp\n"
- "\nSet the local time to given timestamp (-regtest only)\n"
- "\nArguments:\n"
- "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
- " Pass 0 to go back to using the system time."
+ RPCHelpMan{"setmocktime",
+ "\nSet the local time to given timestamp (-regtest only)\n",
+ {
+ {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n"
+ " Pass 0 to go back to using the system time."},
+ }}
+ .ToString()
);
if (!Params().MineBlocksOnDemand())
@@ -299,12 +308,14 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
*/
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getmemoryinfo (\"mode\")\n"
- "Returns an object containing information about memory usage.\n"
- "Arguments:\n"
- "1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\n"
+ RPCHelpMan{"getmemoryinfo",
+ "Returns an object containing information about memory usage.\n",
+ {
+ {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"stats\"", "determines what kind of information is returned.\n"
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
- " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+).\n"
+ " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."},
+ }}
+ .ToString() +
"\nResult (mode \"stats\"):\n"
"{\n"
" \"locked\": { (json object) Information about locked memory manager\n"
@@ -361,7 +372,7 @@ UniValue logging(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "logging ( <include> <exclude> )\n"
+ RPCHelpMan{"logging",
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
"When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
@@ -371,17 +382,18 @@ UniValue logging(const JSONRPCRequest& request)
"In addition, the following are available as category names with special meanings:\n"
" - \"all\", \"1\" : represent all logging categories.\n"
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
- "\nArguments:\n"
- "1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n"
- " [\n"
- " \"category\" (string) the valid logging category\n"
- " ,...\n"
- " ]\n"
- "2. \"exclude\" (array of strings, optional) A json array of categories to remove debug logging\n"
- " [\n"
- " \"category\" (string) the valid logging category\n"
- " ,...\n"
- " ]\n"
+ ,
+ {
+ {"include", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to add debug logging",
+ {
+ {"include_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ }},
+ {"exclude", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to remove debug logging",
+ {
+ {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ }},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object where keys are the logging categories, and values indicates its status\n"
" \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n"
@@ -430,11 +442,13 @@ static UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
throw std::runtime_error(
- "echo|echojson \"message\" ...\n"
- "\nSimply echo back the input arguments. This command is for testing.\n"
- "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in"
- "bitcoin-cli and the GUI. There is no server-side difference."
- );
+ RPCHelpMan{"echo|echojson ...",
+ "\nSimply echo back the input arguments. This command is for testing.\n"
+ "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
+ "bitcoin-cli and the GUI. There is no server-side difference.",
+ {}}
+ .ToString() +
+ "");
return request.params;
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 4d893bb838..4d6b260cc7 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -7,17 +7,18 @@
#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
-#include <validation.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
#include <rpc/protocol.h>
+#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
#include <ui_interface.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <version.h>
#include <warnings.h>
@@ -27,8 +28,9 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getconnectioncount\n"
- "\nReturns the number of connections to other nodes.\n"
+ RPCHelpMan{"getconnectioncount",
+ "\nReturns the number of connections to other nodes.\n", {}}
+ .ToString() +
"\nResult:\n"
"n (numeric) The connection count\n"
"\nExamples:\n"
@@ -46,10 +48,12 @@ static UniValue ping(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "ping\n"
- "\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
- "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
- "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n"
+ RPCHelpMan{"ping",
+ "\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
+ "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
+ "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n",
+ {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("ping", "")
+ HelpExampleRpc("ping", "")
@@ -69,8 +73,9 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getpeerinfo\n"
- "\nReturns data about each connected network node as a json array of objects.\n"
+ RPCHelpMan{"getpeerinfo",
+ "\nReturns data about each connected network node as a json array of objects.\n", {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -200,14 +205,16 @@ static UniValue addnode(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "addnode \"node\" \"add|remove|onetry\"\n"
- "\nAttempts to add or remove a node from the addnode list.\n"
- "Or try a connection to a node once.\n"
- "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
- "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n"
- "\nArguments:\n"
- "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
- "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n"
+ RPCHelpMan{"addnode",
+ "\nAttempts to add or remove a node from the addnode list.\n"
+ "Or try a connection to a node once.\n"
+ "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
+ "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n",
+ {
+ {"node", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The node (see getpeerinfo for nodes)"},
+ {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"")
+ HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
@@ -243,13 +250,15 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
- "disconnectnode \"[address]\" [nodeid]\n"
- "\nImmediately disconnects from the specified peer node.\n"
- "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
- "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
- "\nArguments:\n"
- "1. \"address\" (string, optional) The IP address/port of the node\n"
- "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
+ RPCHelpMan{"disconnectnode",
+ "\nImmediately disconnects from the specified peer node.\n"
+ "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
+ "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "fallback to nodeid", "The IP address/port of the node"},
+ {"nodeid", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleCli("disconnectnode", "\"\" 1")
@@ -286,11 +295,13 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getaddednodeinfo ( \"node\" )\n"
- "\nReturns information about the given added node, or all added nodes\n"
- "(note that onetry addnodes are not listed here)\n"
- "\nArguments:\n"
- "1. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n"
+ RPCHelpMan{"getaddednodeinfo",
+ "\nReturns information about the given added node, or all added nodes\n"
+ "(note that onetry addnodes are not listed here)\n",
+ {
+ {"node", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
+ }}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -353,9 +364,11 @@ static UniValue getnettotals(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "getnettotals\n"
- "\nReturns information about network traffic, including bytes in, bytes out,\n"
- "and current time.\n"
+ RPCHelpMan{"getnettotals",
+ "\nReturns information about network traffic, including bytes in, bytes out,\n"
+ "and current time.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
@@ -419,8 +432,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getnetworkinfo\n"
- "Returns an object containing various state info regarding P2P networking.\n"
+ RPCHelpMan{"getnetworkinfo",
+ "Returns an object containing various state info regarding P2P networking.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"version\": xxxxx, (numeric) the server version\n"
@@ -499,13 +513,15 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n"
- "\nAttempts to add or remove an IP/Subnet from the banned list.\n"
- "\nArguments:\n"
- "1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)\n"
- "2. \"command\" (string, required) 'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list\n"
- "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n"
- "4. \"absolute\" (boolean, optional) If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
+ RPCHelpMan{"setban",
+ "\nAttempts to add or remove an IP/Subnet from the banned list.\n",
+ {
+ {"subnet", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
+ {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"},
+ {"bantime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
+ {"absolute", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
@@ -559,8 +575,9 @@ static UniValue listbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listbanned\n"
- "\nList all banned IPs/Subnets.\n"
+ RPCHelpMan{"listbanned",
+ "\nList all banned IPs/Subnets.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
@@ -592,8 +609,9 @@ static UniValue clearbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "clearbanned\n"
- "\nClear all banned IPs.\n"
+ RPCHelpMan{"clearbanned",
+ "\nClear all banned IPs.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
@@ -610,10 +628,12 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "setnetworkactive true|false\n"
- "\nDisable/enable all p2p network activity.\n"
- "\nArguments:\n"
- "1. \"state\" (boolean, required) true to enable networking, false to disable\n"
+ RPCHelpMan{"setnetworkactive",
+ "\nDisable/enable all p2p network activity.\n",
+ {
+ {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"},
+ }}
+ .ToString()
);
}
@@ -630,11 +650,12 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
- "getnodeaddresses ( count )\n"
- "\nReturn known addresses which can potentially be used to find new nodes in the network\n"
- "\nArguments:\n"
- "1. \"count\" (numeric, optional) How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) +
- " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses. (default = 1)\n"
+ RPCHelpMan{"getnodeaddresses",
+ "\nReturn known addresses which can potentially be used to find new nodes in the network\n",
+ {
+ {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."},
+ }}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index c27d5d142b..370ae8e4d7 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,10 +10,8 @@
#include <core_io.h>
#include <index/txindex.h>
#include <init.h>
-#include <keystore.h>
-#include <validation.h>
-#include <validationinterface.h>
#include <key_io.h>
+#include <keystore.h>
#include <merkleblock.h>
#include <net.h>
#include <policy/policy.h>
@@ -29,6 +27,8 @@
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
+#include <validation.h>
+#include <validationinterface.h>
#include <future>
#include <stdint.h>
@@ -66,24 +66,24 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
-
- "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
- "enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
- "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
- "provided, only that block will be searched and if the transaction is in the mempool or other\n"
- "blocks, or if this node does not have the given block available, the transaction will not be found.\n"
+ RPCHelpMan{"getrawtransaction",
+ "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
+ "enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
+ "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
+ "provided, only that block will be searched and if the transaction is in the mempool or other\n"
+ "blocks, or if this node does not have the given block available, the transaction will not be found.\n"
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
"\nReturn the raw transaction data.\n"
"\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
"If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n"
-
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
- "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
- "3. \"blockhash\" (string, optional) The block in which to look for the transaction\n"
-
+ ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If false, return a string, otherwise return a json object"},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "The block in which to look for the transaction"},
+ }}
+ .ToString() +
"\nResult (if verbose is not set or set to false):\n"
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
@@ -206,19 +206,21 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error(
- "gettxoutproof [\"txid\",...] ( blockhash )\n"
- "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
- "\nNOTE: By default this function only works sometimes. This is when there is an\n"
- "unspent output in the utxo for this transaction. To make it always work,\n"
- "you need to maintain a transaction index, using the -txindex command line option or\n"
- "specify the block in which the transaction is included manually (by blockhash).\n"
- "\nArguments:\n"
- "1. \"txids\" (string) A json array of txids to filter\n"
- " [\n"
- " \"txid\" (string) A transaction hash\n"
- " ,...\n"
- " ]\n"
- "2. \"blockhash\" (string, optional) If specified, looks for txid in the block with this hash\n"
+ RPCHelpMan{"gettxoutproof",
+ "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
+ "\nNOTE: By default this function only works sometimes. This is when there is an\n"
+ "unspent output in the utxo for this transaction. To make it always work,\n"
+ "you need to maintain a transaction index, using the -txindex command line option or\n"
+ "specify the block in which the transaction is included manually (by blockhash).\n",
+ {
+ {"txids", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of txids to filter",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ },
+ },
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "If specified, looks for txid in the block with this hash"},
+ }}
+ .ToString() +
"\nResult:\n"
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
);
@@ -298,11 +300,13 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "verifytxoutproof \"proof\"\n"
- "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
- "and throwing an RPC error if the block is not in our best chain\n"
- "\nArguments:\n"
- "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n"
+ RPCHelpMan{"verifytxoutproof",
+ "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
+ "and throwing an RPC error if the block is not in our best chain\n",
+ {
+ {"proof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded proof generated by gettxoutproof"},
+ }}
+ .ToString() +
"\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
);
@@ -393,7 +397,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vin.push_back(in);
}
- std::set<CTxDestination> destinations;
if (!outputs_is_obj) {
// Translate array of key-value pairs into dict
UniValue outputs_dict = UniValue(UniValue::VOBJ);
@@ -409,8 +412,17 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
}
outputs = std::move(outputs_dict);
}
+
+ // Duplicate checking
+ std::set<CTxDestination> destinations;
+ bool has_data{false};
+
for (const std::string& name_ : outputs.getKeys()) {
if (name_ == "data") {
+ if (has_data) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
+ }
+ has_data = true;
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
CTxOut out(0, CScript() << OP_RETURN << data);
@@ -444,38 +456,46 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
throw std::runtime_error(
- // clang-format off
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
- "\nCreate a transaction spending the given inputs and creating new outputs.\n"
- "Outputs can be addresses or data.\n"
- "Returns hex-encoded raw transaction.\n"
- "Note that the transaction's inputs are not signed, and\n"
- "it is not stored in the wallet or transmitted to the network.\n"
-
- "\nArguments:\n"
- "1. \"inputs\" (array, required) A json array of json objects\n"
- " [\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"sequence\":n (numeric, optional) The sequence number\n"
- " } \n"
- " ,...\n"
- " ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125-replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ RPCHelpMan{"createrawtransaction",
+ "\nCreate a transaction spending the given inputs and creating new outputs.\n"
+ "Outputs can be addresses or data.\n"
+ "Returns hex-encoded raw transaction.\n"
+ "Note that the transaction's inputs are not signed, and\n"
+ "it is not stored in the wallet or transmitted to the network.\n",
+ {
+ {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ },
+ },
+ },
+ },
+ {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
+ "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
+ },
+ },
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ },
+ },
+ {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125-replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
+ }}
+ .ToString() +
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
@@ -484,7 +504,6 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
- // clang-format on
);
}
@@ -505,14 +524,14 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "decoderawtransaction \"hexstring\" ( iswitness )\n"
- "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
-
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The transaction hex string\n"
- "2. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction\n"
- " If iswitness is not present, heuristic tests will be used in decoding\n"
-
+ RPCHelpMan{"decoderawtransaction",
+ "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction hex string"},
+ {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n"
+ " If iswitness is not present, heuristic tests will be used in decoding"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
@@ -580,10 +599,12 @@ static UniValue decodescript(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "decodescript \"hexstring\"\n"
- "\nDecode a hex-encoded script.\n"
- "\nArguments:\n"
- "1. \"hexstring\" (string) the hex-encoded script\n"
+ RPCHelpMan{"decodescript",
+ "\nDecode a hex-encoded script.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded script"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
@@ -673,21 +694,20 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
-
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinerawtransaction [\"hexstring\",...]\n"
- "\nCombine multiple partially signed transactions into one transaction.\n"
- "The combined transaction may be another partially signed transaction or a \n"
- "fully signed transaction."
-
- "\nArguments:\n"
- "1. \"txs\" (string) A json array of hex strings of partially signed transactions\n"
- " [\n"
- " \"hexstring\" (string) A transaction hash\n"
- " ,...\n"
- " ]\n"
-
+ RPCHelpMan{"combinerawtransaction",
+ "\nCombine multiple partially signed transactions into one transaction.\n"
+ "The combined transaction may be another partially signed transaction or a \n"
+ "fully signed transaction.",
+ {
+ {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex strings of partially signed transactions",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ },
+ },
+ }}
+ .ToString() +
"\nResult:\n"
"\"hex\" (string) The hex-encoded raw transaction with signature(s)\n"
@@ -899,39 +919,42 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second argument is an array of base58-encoded private\n"
- "keys that will be the only keys used to sign the transaction.\n"
- "The third optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block chain.\n"
-
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The transaction hex string\n"
- "2. \"privkeys\" (string, required) A json array of base58-encoded private keys for signing\n"
- " [ (json array of strings)\n"
- " \"privatekey\" (string) private key in base58-encoding\n"
- " ,...\n"
- " ]\n"
- "3. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
- " [ (json array of json objects, or 'null' if none provided)\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"scriptPubKey\": \"hex\", (string, required) script key\n"
- " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
- " \"amount\": value (numeric, required) The amount spent\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of:\n"
+ RPCHelpMan{"signrawtransactionwithkey",
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second argument is an array of base58-encoded private\n"
+ "keys that will be the only keys used to sign the transaction.\n"
+ "The third optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n",
+ {
+ {"hexstring", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction hex string"},
+ {"privkeys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base58-encoded private keys for signing",
+ {
+ {"privatekey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "private key in base58-encoding"},
+ },
+ },
+ {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "script key"},
+ {"redeemScript", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "omitted", "(required for P2SH or P2WSH) redeem script"},
+ {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount spent"},
+ },
+ },
+ },
+ },
+ {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type. Must be one of:\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\n"
-
+ },
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
@@ -986,12 +1009,14 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "sendrawtransaction \"hexstring\" ( allowhighfees )\n"
- "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
- "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n"
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
- "2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
+ RPCHelpMan{"sendrawtransaction",
+ "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
+ "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"},
+ {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"},
+ }}
+ .ToString() +
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
@@ -1081,15 +1106,20 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- // clang-format off
- "testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n"
- "\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
- "\nThis checks if the transaction violates the consensus or policy rules.\n"
- "\nSee sendrawtransaction call.\n"
- "\nArguments:\n"
- "1. [\"rawtxs\"] (array, required) An array of hex strings of raw transactions.\n"
- " Length must be one for now.\n"
- "2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
+ RPCHelpMan{"testmempoolaccept",
+ "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
+ "\nThis checks if the transaction violates the consensus or policy rules.\n"
+ "\nSee sendrawtransaction call.\n",
+ {
+ {"rawtxs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "An array of hex strings of raw transactions.\n"
+ " Length must be one for now.",
+ {
+ {"rawtx", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", ""},
+ },
+ },
+ {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"},
+ }}
+ .ToString() +
"\nResult:\n"
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
" Length is exactly one for now.\n"
@@ -1108,7 +1138,6 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
- // clang-format on
);
}
@@ -1179,12 +1208,12 @@ UniValue decodepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "decodepsbt \"psbt\"\n"
- "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n"
-
- "\nArguments:\n"
- "1. \"psbt\" (string, required) The PSBT base64 string\n"
-
+ RPCHelpMan{"decodepsbt",
+ "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
+ {
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The PSBT base64 string"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
@@ -1454,16 +1483,17 @@ UniValue combinepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinepsbt [\"psbt\",...]\n"
- "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
- "Implements the Combiner role.\n"
- "\nArguments:\n"
- "1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n"
- " [\n"
- " \"psbt\" (string) A base64 string of a PSBT\n"
- " ,...\n"
- " ]\n"
-
+ RPCHelpMan{"combinepsbt",
+ "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
+ "Implements the Combiner role.\n",
+ {
+ {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base64 strings of partially signed transactions",
+ {
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"},
+ },
+ },
+ }}
+ .ToString() +
"\nResult:\n"
" \"psbt\" (string) The base64-encoded partially signed transaction\n"
"\nExamples:\n"
@@ -1507,16 +1537,17 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "finalizepsbt \"psbt\" ( extract )\n"
- "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
- "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
- "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
- "Implements the Finalizer and Extractor roles.\n"
- "\nArguments:\n"
- "1. \"psbt\" (string) A base64 string of a PSBT\n"
- "2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n"
- " extract and return the complete transaction in normal network serialization instead of the PSBT.\n"
-
+ RPCHelpMan{"finalizepsbt",
+ "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
+ "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
+ "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
+ "Implements the Finalizer and Extractor roles.\n",
+ {
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"},
+ {"extract", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "If true and the transaction is complete,\n"
+ " extract and return the complete transaction in normal network serialization instead of the PSBT."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n"
@@ -1571,33 +1602,43 @@ UniValue createpsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
- "\nCreates a transaction in the Partially Signed Transaction format.\n"
- "Implements the Creator role.\n"
- "\nArguments:\n"
- "1. \"inputs\" (array, required) A json array of json objects\n"
- " [\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"sequence\":n (numeric, optional) The sequence number\n"
- " } \n"
- " ,...\n"
- " ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ RPCHelpMan{"createpsbt",
+ "\nCreates a transaction in the Partially Signed Transaction format.\n"
+ "Implements the Creator role.\n",
+ {
+ {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ },
+ },
+ },
+ },
+ {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
+ "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
+ },
+ },
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ },
+ },
+ {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
+ }}
+ .ToString() +
"\nResult:\n"
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
"\nExamples:\n"
@@ -1636,17 +1677,19 @@ UniValue converttopsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n"
- "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
- "createpsbt and walletcreatefundedpsbt should be used for new applications.\n"
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The hex string of a raw transaction\n"
- "2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n"
- " will continue. If false, RPC will fail if any signatures are present.\n"
- "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n"
+ RPCHelpMan{"converttopsbt",
+ "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
+ "createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of a raw transaction"},
+ {"permitsigdata", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, any signatures in the input will be discarded and conversion.\n"
+ " will continue. If false, RPC will fail if any signatures are present."},
+ {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
" If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
" will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n"
- " permitsigdata is true.\n"
+ " permitsigdata is true."},
+ }}
+ .ToString() +
"\nResult:\n"
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
"\nExamples:\n"
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 78abd7b610..733f8601ee 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -8,11 +8,12 @@
#include <fs.h>
#include <key_io.h>
#include <random.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
@@ -141,10 +142,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
-/**
- * Note: This interface may still be subject to change.
- */
-
std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
{
std::string strRet;
@@ -203,10 +200,12 @@ UniValue help(const JSONRPCRequest& jsonRequest)
{
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
- "help ( \"command\" )\n"
- "\nList all commands, or get help for a specified command.\n"
- "\nArguments:\n"
- "1. \"command\" (string, optional) The command to get help on\n"
+ RPCHelpMan{"help",
+ "\nList all commands, or get help for a specified command.\n",
+ {
+ {"command", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all commands", "The command to get help on"},
+ }}
+ .ToString() +
"\nResult:\n"
"\"text\" (string) The help text\n"
);
@@ -222,22 +221,30 @@ UniValue help(const JSONRPCRequest& jsonRequest)
UniValue stop(const JSONRPCRequest& jsonRequest)
{
// Accept the deprecated and ignored 'detach' boolean argument
+ // Also accept the hidden 'wait' integer argument (milliseconds)
+ // For instance, 'stop 1000' makes the call wait 1 second before returning
+ // to the client (intended for testing)
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
- "stop\n"
- "\nStop Bitcoin server.");
+ RPCHelpMan{"stop",
+ "\nStop Bitcoin server.", {}}
+ .ToString());
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
+ if (jsonRequest.params[0].isNum()) {
+ MilliSleep(jsonRequest.params[0].get_int());
+ }
return "Bitcoin server stopping";
}
static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 0)
throw std::runtime_error(
- "uptime\n"
- "\nReturns the total uptime of the server.\n"
+ RPCHelpMan{"uptime",
+ "\nReturns the total uptime of the server.\n", {}}
+ .ToString() +
"\nResult:\n"
"ttt (numeric) The number of seconds that the server has been running\n"
"\nExamples:\n"
@@ -249,15 +256,12 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
}
// clang-format off
-/**
- * Call Table
- */
static const CRPCCommand vRPCCommands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
/* Overall control/query calls */
{ "control", "help", &help, {"command"} },
- { "control", "stop", &stop, {} },
+ { "control", "stop", &stop, {"wait"} },
{ "control", "uptime", &uptime, {} },
};
// clang-format on
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ef2d14b90e..740f8351fe 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -128,3 +128,306 @@ UniValue DescribeAddress(const CTxDestination& dest)
{
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
+
+struct Section {
+ Section(const std::string& left, const std::string& right)
+ : m_left{left}, m_right{right} {}
+ const std::string m_left;
+ const std::string m_right;
+};
+
+struct Sections {
+ std::vector<Section> m_sections;
+ size_t m_max_pad{0};
+
+ void PushSection(const Section& s)
+ {
+ m_max_pad = std::max(m_max_pad, s.m_left.size());
+ m_sections.push_back(s);
+ }
+
+ enum class OuterType {
+ ARR,
+ OBJ,
+ NAMED_ARG, // Only set on first recursion
+ };
+
+ void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
+ {
+ const auto indent = std::string(current_indent, ' ');
+ const auto indent_next = std::string(current_indent + 2, ' ');
+ switch (arg.m_type) {
+ case RPCArg::Type::STR_HEX:
+ case RPCArg::Type::STR:
+ case RPCArg::Type::NUM:
+ case RPCArg::Type::AMOUNT:
+ case RPCArg::Type::BOOL: {
+ if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
+ auto left = indent;
+ if (arg.m_type_str.size() != 0 && outer_type == OuterType::OBJ) {
+ left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
+ } else {
+ left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
+ }
+ left += ",";
+ PushSection({left, arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR)});
+ break;
+ }
+ case RPCArg::Type::OBJ:
+ case RPCArg::Type::OBJ_USER_KEYS: {
+ const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR);
+ PushSection({indent + "{", right});
+ for (const auto& arg_inner : arg.m_inner) {
+ Push(arg_inner, current_indent + 2, OuterType::OBJ);
+ }
+ if (arg.m_type != RPCArg::Type::OBJ) {
+ PushSection({indent_next + "...", ""});
+ }
+ PushSection({indent + "}" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
+ break;
+ }
+ case RPCArg::Type::ARR: {
+ auto left = indent;
+ left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : "";
+ left += "[";
+ const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR);
+ PushSection({left, right});
+ for (const auto& arg_inner : arg.m_inner) {
+ Push(arg_inner, current_indent + 2, OuterType::ARR);
+ }
+ PushSection({indent_next + "...", ""});
+ PushSection({indent + "]" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
+ break;
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ }
+
+ std::string ToString() const
+ {
+ std::string ret;
+ const size_t pad = m_max_pad + 4;
+ for (const auto& s : m_sections) {
+ if (s.m_right.empty()) {
+ ret += s.m_left;
+ ret += "\n";
+ continue;
+ }
+
+ std::string left = s.m_left;
+ left.resize(pad, ' ');
+ ret += left;
+
+ // Properly pad after newlines
+ std::string right;
+ size_t begin = 0;
+ size_t new_line_pos = s.m_right.find_first_of('\n');
+ while (true) {
+ right += s.m_right.substr(begin, new_line_pos - begin);
+ if (new_line_pos == std::string::npos) {
+ break; //No new line
+ }
+ right += "\n" + std::string(pad, ' ');
+ begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
+ if (begin == std::string::npos) {
+ break; // Empty line
+ }
+ new_line_pos = s.m_right.find_first_of('\n', begin + 1);
+ }
+ ret += right;
+ ret += "\n";
+ }
+ return ret;
+ }
+};
+
+RPCHelpMan::RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args)
+ : m_name{name}, m_description{description}, m_args{args}
+{
+ std::set<std::string> named_args;
+ for (const auto& arg : m_args) {
+ // Should have unique named arguments
+ assert(named_args.insert(arg.m_name).second);
+ }
+}
+
+std::string RPCHelpMan::ToString() const
+{
+ std::string ret;
+
+ // Oneline summary
+ ret += m_name;
+ bool is_optional{false};
+ for (const auto& arg : m_args) {
+ ret += " ";
+ if (arg.m_optional) {
+ if (!is_optional) ret += "( ";
+ is_optional = true;
+ } else {
+ // Currently we still support unnamed arguments, so any argument following an optional argument must also be optional
+ // If support for positional arguments is deprecated in the future, remove this line
+ assert(!is_optional);
+ }
+ ret += arg.ToString(/* oneline */ true);
+ }
+ if (is_optional) ret += " )";
+ ret += "\n";
+
+ // Description
+ ret += m_description;
+
+ // Arguments
+ Sections sections;
+ for (size_t i{0}; i < m_args.size(); ++i) {
+ const auto& arg = m_args.at(i);
+
+ if (i == 0) ret += "\nArguments:\n";
+
+ // Push named argument name and description
+ const auto str_wrapper = (arg.m_type == RPCArg::Type::STR || arg.m_type == RPCArg::Type::STR_HEX) ? "\"" : "";
+ sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + str_wrapper + arg.m_name + str_wrapper, arg.ToDescriptionString());
+ sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
+
+ // Recursively push nested args
+ sections.Push(arg);
+ }
+ ret += sections.ToString();
+
+ return ret;
+}
+
+std::string RPCArg::ToDescriptionString(const bool implicitly_required) const
+{
+ std::string ret;
+ ret += "(";
+ if (m_type_str.size() != 0) {
+ ret += m_type_str.at(1);
+ } else {
+ switch (m_type) {
+ case Type::STR_HEX:
+ case Type::STR: {
+ ret += "string";
+ break;
+ }
+ case Type::NUM: {
+ ret += "numeric";
+ break;
+ }
+ case Type::AMOUNT: {
+ ret += "numeric or string";
+ break;
+ }
+ case Type::BOOL: {
+ ret += "boolean";
+ break;
+ }
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS: {
+ ret += "json object";
+ break;
+ }
+ case Type::ARR: {
+ ret += "json array";
+ break;
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ }
+ if (!implicitly_required) {
+ ret += ", ";
+ if (m_optional) {
+ ret += "optional";
+ if (!m_default_value.empty()) {
+ ret += ", default=" + m_default_value;
+ } else {
+ // TODO enable this assert, when all optional parameters have their default value documented
+ //assert(false);
+ }
+ } else {
+ ret += "required";
+ assert(m_default_value.empty()); // Default value is ignored, and must not be present
+ }
+ }
+ ret += ")";
+ ret += m_description.empty() ? "" : " " + m_description;
+ return ret;
+}
+
+std::string RPCArg::ToStringObj(const bool oneline) const
+{
+ std::string res;
+ res += "\"";
+ res += m_name;
+ if (oneline) {
+ res += "\":";
+ } else {
+ res += "\": ";
+ }
+ switch (m_type) {
+ case Type::STR:
+ return res + "\"str\"";
+ case Type::STR_HEX:
+ return res + "\"hex\"";
+ case Type::NUM:
+ return res + "n";
+ case Type::AMOUNT:
+ return res + "amount";
+ case Type::BOOL:
+ return res + "bool";
+ case Type::ARR:
+ res += "[";
+ for (const auto& i : m_inner) {
+ res += i.ToString(oneline) + ",";
+ }
+ return res + "...]";
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS:
+ // Currently unused, so avoid writing dead code
+ assert(false);
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+std::string RPCArg::ToString(const bool oneline) const
+{
+ if (oneline && !m_oneline_description.empty()) return m_oneline_description;
+
+ switch (m_type) {
+ case Type::STR_HEX:
+ case Type::STR: {
+ return "\"" + m_name + "\"";
+ }
+ case Type::NUM:
+ case Type::AMOUNT:
+ case Type::BOOL: {
+ return m_name;
+ }
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS: {
+ std::string res;
+ for (size_t i = 0; i < m_inner.size();) {
+ res += m_inner[i].ToStringObj(oneline);
+ if (++i < m_inner.size()) res += ",";
+ }
+ if (m_type == Type::OBJ) {
+ return "{" + res + "}";
+ } else {
+ return "{" + res + ",...}";
+ }
+ }
+ case Type::ARR: {
+ std::string res;
+ for (const auto& i : m_inner) {
+ res += i.ToString(oneline) + ",";
+ }
+ return "[" + res + "...]";
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e21b5ba22a..9e97b3ae0b 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -9,8 +9,6 @@
#include <script/standard.h>
#include <univalue.h>
-#include <boost/variant/static_visitor.hpp>
-
#include <string>
#include <vector>
@@ -30,4 +28,95 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
UniValue DescribeAddress(const CTxDestination& dest);
+struct RPCArg {
+ enum class Type {
+ OBJ,
+ ARR,
+ STR,
+ NUM,
+ BOOL,
+ OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
+ AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
+ STR_HEX, //!< Special type that is a STR with only hex chars
+ };
+ const std::string m_name; //!< The name of the arg (can be empty for inner args)
+ const Type m_type;
+ const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
+ const bool m_optional;
+ const std::string m_default_value; //!< Only used for optional args
+ const std::string m_description;
+ const std::string m_oneline_description; //!< Should be empty unless it is supposed to override the auto-generated summary line
+ const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description.
+
+ RPCArg(
+ const std::string& name,
+ const Type& type,
+ const bool opt,
+ const std::string& default_val,
+ const std::string& description,
+ const std::string& oneline_description = "",
+ const std::vector<std::string>& type_str = {})
+ : m_name{name},
+ m_type{type},
+ m_optional{opt},
+ m_default_value{default_val},
+ m_description{description},
+ m_oneline_description{oneline_description},
+ m_type_str{type_str}
+ {
+ assert(type != Type::ARR && type != Type::OBJ);
+ }
+
+ RPCArg(
+ const std::string& name,
+ const Type& type,
+ const bool opt,
+ const std::string& default_val,
+ const std::string& description,
+ const std::vector<RPCArg>& inner,
+ const std::string& oneline_description = "",
+ const std::vector<std::string>& type_str = {})
+ : m_name{name},
+ m_type{type},
+ m_inner{inner},
+ m_optional{opt},
+ m_default_value{default_val},
+ m_description{description},
+ m_oneline_description{oneline_description},
+ m_type_str{type_str}
+ {
+ assert(type == Type::ARR || type == Type::OBJ);
+ }
+
+ /**
+ * Return the type string of the argument.
+ * Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description).
+ */
+ std::string ToString(bool oneline) const;
+ /**
+ * Return the type string of the argument when it is in an object (dict).
+ * Set oneline to get the oneline representation (less whitespace)
+ */
+ std::string ToStringObj(bool oneline) const;
+ /**
+ * Return the description string, including the argument type and whether
+ * the argument is required.
+ * implicitly_required is set for arguments in an array, which are neither optional nor required.
+ */
+ std::string ToDescriptionString(bool implicitly_required = false) const;
+};
+
+class RPCHelpMan
+{
+public:
+ RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args);
+
+ std::string ToString() const;
+
+private:
+ const std::string m_name;
+ const std::string m_description;
+ const std::vector<RPCArg> m_args;
+};
+
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 90c4ddcc11..a702be5b78 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -40,8 +40,8 @@ struct PubkeyProvider
{
virtual ~PubkeyProvider() = default;
- /** Derive a public key. */
- virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const = 0;
+ /** Derive a public key. If key==nullptr, only info is desired. */
+ virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
@@ -68,7 +68,7 @@ class OriginPubkeyProvider final : public PubkeyProvider
public:
OriginPubkeyProvider(KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : m_origin(std::move(info)), m_provider(std::move(provider)) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
@@ -94,9 +94,9 @@ class ConstPubkeyProvider final : public PubkeyProvider
public:
ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
- key = m_pubkey;
+ if (key) *key = m_pubkey;
info.path.clear();
CKeyID keyid = m_pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
@@ -152,26 +152,28 @@ public:
BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
- if (IsHardened()) {
- CExtKey extkey;
- if (!GetExtKey(arg, extkey)) return false;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
+ if (key) {
+ if (IsHardened()) {
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ *key = extkey.Neuter().pubkey;
+ } else {
+ // TODO: optimize by caching
+ CExtPubKey extkey = m_extkey;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ assert(m_derive != DeriveType::HARDENED);
+ *key = extkey.pubkey;
}
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
- key = extkey.Neuter().pubkey;
- } else {
- // TODO: optimize by caching
- CExtPubKey extkey = m_extkey;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
- }
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- assert(m_derive != DeriveType::HARDENED);
- key = extkey.pubkey;
}
CKeyID keyid = m_extkey.pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
@@ -202,124 +204,119 @@ public:
}
};
-/** A parsed addr(A) descriptor. */
-class AddressDescriptor final : public Descriptor
+/** Base class for all Descriptor implementations. */
+class DescriptorImpl : public Descriptor
{
- CTxDestination m_destination;
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size of Multisig).
+ const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
+ //! The sub-descriptor argument (nullptr for everything but SH and WSH).
+ const std::unique_ptr<DescriptorImpl> m_script_arg;
+ //! The string name of the descriptor function.
+ const std::string m_name;
+
+protected:
+ //! Return a serialization of anything except pubkey and script arguments, to be prepended to those.
+ virtual std::string ToStringExtra() const { return ""; }
+
+ /** A helper function to construct the scripts for this descriptor.
+ *
+ * This function is invoked once for every CScript produced by evaluating
+ * m_script_arg, or just once in case m_script_arg is nullptr.
+
+ * @param pubkeys The evaluations of the m_pubkey_args field.
+ * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
+ * @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
+ * The script and pubkeys argument to this function are automatically added.
+ * @return A vector with scriptPubKeys for this descriptor.
+ */
+ virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), m_name(name) {}
- bool IsRange() const override { return false; }
- std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+ bool IsSolvable() const override
{
- output_scripts = std::vector<CScript>{GetScriptForDestination(m_destination)};
- return true;
- }
-};
-
-/** A parsed raw(H) descriptor. */
-class RawDescriptor final : public Descriptor
-{
- CScript m_script;
-
-public:
- RawDescriptor(CScript script) : m_script(std::move(script)) {}
-
- bool IsRange() const override { return false; }
- std::string ToString() const override { return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- output_scripts = std::vector<CScript>{m_script};
- return true;
- }
-};
-
-/** A parsed pk(P), pkh(P), or wpkh(P) descriptor. */
-class SingleKeyDescriptor final : public Descriptor
-{
- const std::function<CScript(const CPubKey&)> m_script_fn;
- const std::string m_fn_name;
- std::unique_ptr<PubkeyProvider> m_provider;
-
-public:
- SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {}
-
- bool IsRange() const override { return m_provider->IsRange(); }
- std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_provider->ToPrivateString(arg, ret)) return false;
- out = m_fn_name + "(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- CPubKey key;
- KeyOriginInfo info;
- if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
- output_scripts = std::vector<CScript>{m_script_fn(key)};
- out.origins.emplace(key.GetID(), std::move(info));
- out.pubkeys.emplace(key.GetID(), key);
+ if (m_script_arg) {
+ if (!m_script_arg->IsSolvable()) return false;
+ }
return true;
}
-};
-
-CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); }
-CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); }
-CScript P2WPKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID())); }
-
-/** A parsed multi(...) descriptor. */
-class MultisigDescriptor : public Descriptor
-{
- int m_threshold;
- std::vector<std::unique_ptr<PubkeyProvider>> m_providers;
-
-public:
- MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : m_threshold(threshold), m_providers(std::move(providers)) {}
- bool IsRange() const override
+ bool IsRange() const final
{
- for (const auto& p : m_providers) {
- if (p->IsRange()) return true;
+ for (const auto& pubkey : m_pubkey_args) {
+ if (pubkey->IsRange()) return true;
+ }
+ if (m_script_arg) {
+ if (m_script_arg->IsRange()) return true;
}
return false;
}
- std::string ToString() const override
+ bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv) const
{
- std::string ret = strprintf("multi(%i", m_threshold);
- for (const auto& p : m_providers) {
- ret += "," + p->ToString();
+ std::string extra = ToStringExtra();
+ size_t pos = extra.size() > 0 ? 1 : 0;
+ std::string ret = m_name + "(" + extra;
+ for (const auto& pubkey : m_pubkey_args) {
+ if (pos++) ret += ",";
+ std::string tmp;
+ if (priv) {
+ if (!pubkey->ToPrivateString(*arg, tmp)) return false;
+ } else {
+ tmp = pubkey->ToString();
+ }
+ ret += std::move(tmp);
}
- return std::move(ret) + ")";
- }
-
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret = strprintf("multi(%i", m_threshold);
- for (const auto& p : m_providers) {
- std::string sub;
- if (!p->ToPrivateString(arg, sub)) return false;
- ret += "," + std::move(sub);
+ if (m_script_arg) {
+ if (pos++) ret += ",";
+ std::string tmp;
+ if (!m_script_arg->ToStringHelper(arg, tmp, priv)) return false;
+ ret += std::move(tmp);
}
out = std::move(ret) + ")";
return true;
}
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+ std::string ToString() const final
+ {
+ std::string ret;
+ ToStringHelper(nullptr, ret, false);
+ return ret;
+ }
+
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final { return ToStringHelper(&arg, out, true); }
+
+ bool ExpandHelper(int pos, const SigningProvider& arg, Span<const unsigned char>* cache_read, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache_write) const
{
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
- entries.reserve(m_providers.size());
- // Construct temporary data in `entries`, to avoid producing output in case of failure.
- for (const auto& p : m_providers) {
+ entries.reserve(m_pubkey_args.size());
+
+ // Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
+ for (const auto& p : m_pubkey_args) {
entries.emplace_back();
- if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second)) return false;
+ if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
+ if (cache_read) {
+ // Cached expanded public key exists, use it.
+ if (cache_read->size() == 0) return false;
+ bool compressed = ((*cache_read)[0] == 0x02 || (*cache_read)[0] == 0x03) && cache_read->size() >= 33;
+ bool uncompressed = ((*cache_read)[0] == 0x04) && cache_read->size() >= 65;
+ if (!(compressed || uncompressed)) return false;
+ CPubKey pubkey(cache_read->begin(), cache_read->begin() + (compressed ? 33 : 65));
+ entries.back().first = pubkey;
+ *cache_read = cache_read->subspan(compressed ? 33 : 65);
+ }
+ if (cache_write) {
+ cache_write->insert(cache_write->end(), entries.back().first.begin(), entries.back().first.end());
+ }
}
+ std::vector<CScript> subscripts;
+ if (m_script_arg) {
+ FlatSigningProvider subprovider;
+ if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
+ out = Merge(out, subprovider);
+ }
+
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
for (auto& entry : entries) {
@@ -327,87 +324,141 @@ public:
out.origins.emplace(entry.first.GetID(), std::move(entry.second));
out.pubkeys.emplace(entry.first.GetID(), entry.first);
}
- output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
+ if (m_script_arg) {
+ for (const auto& subscript : subscripts) {
+ out.scripts.emplace(CScriptID(subscript), subscript);
+ std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
+ for (auto& addscript : addscripts) {
+ output_scripts.push_back(std::move(addscript));
+ }
+ }
+ } else {
+ output_scripts = MakeScripts(pubkeys, nullptr, out);
+ }
return true;
}
+
+ bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const final
+ {
+ return ExpandHelper(pos, provider, nullptr, output_scripts, out, cache);
+ }
+
+ bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const final
+ {
+ Span<const unsigned char> span = MakeSpan(cache);
+ return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
+ }
};
-/** A parsed sh(S) or wsh(S) descriptor. */
-class ConvertorDescriptor : public Descriptor
+/** Construct a vector with one element, which is moved into it. */
+template<typename T>
+std::vector<T> Singleton(T elem)
{
- const std::function<CScript(const CScript&)> m_convert_fn;
- const std::string m_fn_name;
- std::unique_ptr<Descriptor> m_descriptor;
+ std::vector<T> ret;
+ ret.emplace_back(std::move(elem));
+ return ret;
+}
+/** A parsed addr(A) descriptor. */
+class AddressDescriptor final : public DescriptorImpl
+{
+ const CTxDestination m_destination;
+protected:
+ std::string ToStringExtra() const override { return EncodeDestination(m_destination); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(m_destination)); }
public:
- ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {}
+ AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
+ bool IsSolvable() const final { return false; }
+};
- bool IsRange() const override { return m_descriptor->IsRange(); }
- std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_descriptor->ToPrivateString(arg, ret)) return false;
- out = m_fn_name + "(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- std::vector<CScript> sub;
- if (!m_descriptor->Expand(pos, arg, sub, out)) return false;
- output_scripts.clear();
- for (const auto& script : sub) {
- CScriptID id(script);
- out.scripts.emplace(CScriptID(script), script);
- output_scripts.push_back(m_convert_fn(script));
- }
- return true;
- }
+/** A parsed raw(H) descriptor. */
+class RawDescriptor final : public DescriptorImpl
+{
+ const CScript m_script;
+protected:
+ std::string ToStringExtra() const override { return HexStr(m_script.begin(), m_script.end()); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(m_script); }
+public:
+ RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
+ bool IsSolvable() const final { return false; }
};
-CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); }
-CScript ConvertP2WSH(const CScript& script) { return GetScriptForDestination(WitnessV0ScriptHash(script)); }
+/** A parsed pk(P) descriptor. */
+class PKDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForRawPubKey(keys[0])); }
+public:
+ PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pk") {}
+};
-/** A parsed combo(P) descriptor. */
-class ComboDescriptor final : public Descriptor
+/** A parsed pkh(P) descriptor. */
+class PKHDescriptor final : public DescriptorImpl
{
- std::unique_ptr<PubkeyProvider> m_provider;
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(keys[0].GetID())); }
+public:
+ PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
+};
+/** A parsed wpkh(P) descriptor. */
+class WPKHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0KeyHash(keys[0].GetID()))); }
public:
- ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {}
+ WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "wpkh") {}
+};
- bool IsRange() const override { return m_provider->IsRange(); }
- std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_provider->ToPrivateString(arg, ret)) return false;
- out = "combo(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+/** A parsed combo(P) descriptor. */
+class ComboDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
{
- CPubKey key;
- KeyOriginInfo info;
- if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
- CKeyID keyid = key.GetID();
- {
- CScript p2pk = GetScriptForRawPubKey(key);
- CScript p2pkh = GetScriptForDestination(keyid);
- output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
- out.pubkeys.emplace(keyid, key);
- out.origins.emplace(keyid, std::move(info));
+ std::vector<CScript> ret;
+ CKeyID id = keys[0].GetID();
+ ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK
+ ret.emplace_back(GetScriptForDestination(id)); // P2PKH
+ if (keys[0].IsCompressed()) {
+ CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(id));
+ out.scripts.emplace(CScriptID(p2wpkh), p2wpkh);
+ ret.emplace_back(p2wpkh);
+ ret.emplace_back(GetScriptForDestination(CScriptID(p2wpkh))); // P2SH-P2WPKH
}
- if (key.IsCompressed()) {
- CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid));
- CScriptID p2wpkh_id(p2wpkh);
- CScript p2sh_p2wpkh = GetScriptForDestination(p2wpkh_id);
- out.scripts.emplace(p2wpkh_id, p2wpkh);
- output_scripts.push_back(std::move(p2wpkh));
- output_scripts.push_back(std::move(p2sh_p2wpkh));
- }
- return true;
+ return ret;
}
+public:
+ ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "combo") {}
+};
+
+/** A parsed multi(...) descriptor. */
+class MultisigDescriptor final : public DescriptorImpl
+{
+ const int m_threshold;
+protected:
+ std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForMultisig(m_threshold, keys)); }
+public:
+ MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : DescriptorImpl(std::move(providers), {}, "multi"), m_threshold(threshold) {}
+};
+
+/** A parsed sh(...) descriptor. */
+class SHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(CScriptID(*script))); }
+public:
+ SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
+};
+
+/** A parsed wsh(...) descriptor. */
+class WSHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
+public:
+ WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
};
////////////////////////////////////////////////////////////////////////////
@@ -478,7 +529,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). */
-bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
+NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
{
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
@@ -555,18 +606,18 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool per
}
/** Parse a script in a particular context. */
-std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
+std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
{
auto expr = Expr(sp);
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk");
+ return MakeUnique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKHGetScript, "pkh");
+ return MakeUnique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(expr, true, out);
@@ -599,17 +650,17 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(expr, false, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2WPKHGetScript, "wpkh");
+ return MakeUnique<WPKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2SH, "sh");
+ return MakeUnique<SHDescriptor>(std::move(desc));
}
if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2WSH, "wsh");
+ return MakeUnique<WSHDescriptor>(std::move(desc));
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
@@ -625,12 +676,91 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
return nullptr;
}
+std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
+{
+ std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
+ return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(key_provider));
+ }
+ return key_provider;
+}
+
+std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ std::vector<std::vector<unsigned char>> data;
+ txnouttype txntype = Solver(script, data);
+
+ if (txntype == TX_PUBKEY) {
+ CPubKey pubkey(data[0].begin(), data[0].end());
+ if (pubkey.IsValid()) {
+ return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_PUBKEYHASH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_MULTISIG) {
+ std::vector<std::unique_ptr<PubkeyProvider>> providers;
+ for (size_t i = 1; i + 1 < data.size(); ++i) {
+ CPubKey pubkey(data[i].begin(), data[i].end());
+ providers.push_back(InferPubkey(pubkey, ctx, provider));
+ }
+ return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
+ }
+ if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
+ uint160 hash(data[0]);
+ CScriptID scriptid(hash);
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
+ if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
+ }
+ }
+ if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ CScriptID scriptid;
+ CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
+ if (sub) return MakeUnique<WSHDescriptor>(std::move(sub));
+ }
+ }
+
+ CTxDestination dest;
+ if (ExtractDestination(script, dest)) {
+ if (GetScriptForDestination(dest) == script) {
+ return MakeUnique<AddressDescriptor>(std::move(dest));
+ }
+ }
+
+ return MakeUnique<RawDescriptor>(script);
+}
+
} // namespace
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
{
Span<const char> sp(descriptor.data(), descriptor.size());
auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
- if (sp.size() == 0 && ret) return ret;
+ if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
}
+
+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 87e07369c7..44f0efca03 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -32,6 +32,10 @@ struct Descriptor {
/** Whether the expansion of this descriptor depends on the position. */
virtual bool IsRange() const = 0;
+ /** Whether this descriptor has all information about signing ignoring lack of private keys.
+ * This is true for all descriptors except ones that use `raw` or `addr` constructions. */
+ virtual bool IsSolvable() const = 0;
+
/** Convert the descriptor back to a string, undoing parsing. */
virtual std::string ToString() const = 0;
@@ -44,12 +48,37 @@ struct Descriptor {
* provider: the provider to query for private keys in case of hardened derivation.
* output_script: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
+ * cache: vector which will be overwritten with cache data necessary to-evaluate the descriptor at this point without access to private keys.
*/
- virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
+ virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
+
+ /** Expand a descriptor at a specified position using cached expansion data.
+ *
+ * pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
+ * cache: vector from which cached expansion data will be read.
+ * output_script: the expanded scriptPubKeys will be put here.
+ * out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
+ */
+ virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
};
/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
-#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
+/** Find a descriptor for the specified script, using information from provider where possible.
+ *
+ * A non-ranged descriptor which only generates the specified script will be returned in all
+ * circumstances.
+ *
+ * For public keys with key origin information, this information will be preserved in the returned
+ * descriptor.
+ *
+ * - If all information for solving `script` is present in `provider`, a descriptor will be returned
+ * which is `IsSolvable()` and encapsulates said information.
+ * - Failing that, if `script` corresponds to a known address type, an "addr()" descriptor will be
+ * returned (which is not `IsSolvable()`).
+ * - Failing that, a "raw()" descriptor is returned.
+ */
+std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider);
+#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 2795dc96d3..635e4fa3d2 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -48,7 +48,7 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig
return false;
}
-static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
+static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
// Look for pubkey in all partial sigs
const auto it = sigdata.signatures.find(address);
@@ -63,14 +63,7 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c
return true;
}
// Query the underlying provider
- if (provider.GetPubKey(address, pubkey)) {
- KeyOriginInfo info;
- if (provider.GetKeyOrigin(address, info)) {
- sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info)));
- }
- return true;
- }
- return false;
+ return provider.GetPubKey(address, pubkey);
}
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
@@ -712,5 +705,7 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
ret.keys = a.keys;
ret.keys.insert(b.keys.begin(), b.keys.end());
+ ret.origins = a.origins;
+ ret.origins.insert(b.origins.begin(), b.origins.end());
return ret;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index e71f43f96d..20c7203b26 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -24,6 +24,11 @@ struct KeyOriginInfo
{
unsigned char fingerprint[4];
std::vector<uint32_t> path;
+
+ friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
+ }
};
/** An interface to be implemented by keystores that support signing. */
@@ -206,6 +211,9 @@ template<typename Stream>
void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type)
{
for (auto keypath_pair : hd_keypaths) {
+ if (!keypath_pair.first.IsValid()) {
+ throw std::ios_base::failure("Invalid CPubKey being serialized");
+ }
SerializeToVector(s, type, MakeSpan(keypath_pair.first));
WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t));
s << keypath_pair.second.fingerprint;
diff --git a/src/streams.h b/src/streams.h
index dc20f7a9da..d5565fe61f 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -761,7 +761,7 @@ protected:
public:
CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
- nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0)
+ nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0)
{
src = fileIn;
}
@@ -846,7 +846,7 @@ public:
// prevent reading beyond a certain position
// no argument removes the limit
- bool SetLimit(uint64_t nPos = (uint64_t)(-1)) {
+ bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {
if (nPos < nReadPos)
return false;
nReadLimit = nPos;
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 8c2873d916..55fe19cebe 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -34,7 +34,7 @@ public:
int RandomInt(int nMax) override
{
- state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash();
+ state = (CHashWriter(SER_GETHASH, 0) << state).GetCheapHash();
return (unsigned int)(state % nMax);
}
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 5131fe8235..309b8d2d06 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -344,4 +344,49 @@ BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
}
+BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
+ // Check that the highest legal index is decoded correctly
+ BlockTransactionsRequest req0;
+ req0.blockhash = InsecureRand256();
+ req0.indexes.resize(1);
+ req0.indexes[0] = 0xffff;
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << req0;
+
+ BlockTransactionsRequest req1;
+ stream >> req1;
+ BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
+ BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
+ // Any set of index deltas that starts with N values that sum to (0x10000 - N)
+ // causes the edge-case overflow that was originally not checked for. Such
+ // a request cannot be created by serializing a real BlockTransactionsRequest
+ // due to the overflow, so here we'll serialize from raw deltas.
+ BlockTransactionsRequest req0;
+ req0.blockhash = InsecureRand256();
+ req0.indexes.resize(3);
+ req0.indexes[0] = 0x7000;
+ req0.indexes[1] = 0x10000 - 0x7000 - 2;
+ req0.indexes[2] = 0;
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << req0.blockhash;
+ WriteCompactSize(stream, req0.indexes.size());
+ WriteCompactSize(stream, req0.indexes[0]);
+ WriteCompactSize(stream, req0.indexes[1]);
+ WriteCompactSize(stream, req0.indexes[2]);
+
+ BlockTransactionsRequest req1;
+ try {
+ stream >> req1;
+ // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
+ BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
+ // this shouldn't be reachable before or after patch
+ BOOST_CHECK(0);
+ } catch(std::ios_base::failure &) {
+ // deserialize should fail
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 521312f1b7..d3cbaedf00 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,17 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <attributes.h>
#include <coins.h>
+#include <consensus/validation.h>
#include <script/standard.h>
+#include <test/test_bitcoin.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
#include <validation.h>
-#include <consensus/validation.h>
-#include <vector>
#include <map>
+#include <vector>
#include <boost/test/unit_test.hpp>
@@ -36,7 +37,7 @@ class CCoinsViewTest : public CCoinsView
std::map<COutPoint, Coin> map_;
public:
- bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
+ NODISCARD bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
{
std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
if (it == map_.end()) {
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 9957ac074b..1034d4ade2 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -102,15 +102,15 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
char key_res;
uint256 val_res;
- it->GetKey(key_res);
- it->GetValue(val_res);
+ BOOST_REQUIRE(it->GetKey(key_res));
+ BOOST_REQUIRE(it->GetValue(val_res));
BOOST_CHECK_EQUAL(key_res, key);
BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());
it->Next();
- it->GetKey(key_res);
- it->GetValue(val_res);
+ BOOST_REQUIRE(it->GetKey(key_res));
+ BOOST_REQUIRE(it->GetValue(val_res));
BOOST_CHECK_EQUAL(key_res, key2);
BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 57e4b067c0..8da8cfc00c 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -62,7 +62,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Check that both versions serialize back to the public version.
std::string pub1 = parse_priv->ToString();
- std::string pub2 = parse_priv->ToString();
+ std::string pub2 = parse_pub->ToString();
BOOST_CHECK_EQUAL(pub, pub1);
BOOST_CHECK_EQUAL(pub, pub2);
@@ -79,19 +79,42 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0);
BOOST_CHECK_EQUAL(parse_priv->IsRange(), (flags & RANGE) != 0);
-
- // Is not ranged descriptor, only a single result is expected.
+ // * For ranged descriptors, the `scripts` parameter is a list of expected result outputs, for subsequent
+ // positions to evaluate the descriptors on (so the first element of `scripts` is for evaluating the
+ // descriptor at 0; the second at 1; and so on). To verify this, we evaluate the descriptors once for
+ // each element in `scripts`.
+ // * For non-ranged descriptors, we evaluate the descriptors at positions 0, 1, and 2, but expect the
+ // same result in each case, namely the first element of `scripts`. Because of that, the size of
+ // `scripts` must be one in that case.
if (!(flags & RANGE)) assert(scripts.size() == 1);
-
size_t max = (flags & RANGE) ? scripts.size() : 3;
+
+ // Iterate over the position we'll evaluate the descriptors in.
for (size_t i = 0; i < max; ++i) {
+ // Call the expected result scripts `ref`.
const auto& ref = scripts[(flags & RANGE) ? i : 0];
+ // When t=0, evaluate the `prv` descriptor; when t=1, evaluate the `pub` descriptor.
for (int t = 0; t < 2; ++t) {
+ // When the descriptor is hardened, evaluate with access to the private keys inside.
const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
- FlatSigningProvider script_provider;
- std::vector<CScript> spks;
- BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider));
+
+ // Evaluate the descriptor selected by `t` in poisition `i`.
+ FlatSigningProvider script_provider, script_provider_cached;
+ std::vector<CScript> spks, spks_cached;
+ std::vector<unsigned char> cache;
+ BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider, &cache));
+
+ // Compare the output with the expected result.
BOOST_CHECK_EQUAL(spks.size(), ref.size());
+
+ // Try to expand again using cached data, and compare.
+ BOOST_CHECK(parse_pub->ExpandFromCache(i, cache, spks_cached, script_provider_cached));
+ BOOST_CHECK(spks == spks_cached);
+ BOOST_CHECK(script_provider.pubkeys == script_provider_cached.pubkeys);
+ BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);
+ BOOST_CHECK(script_provider.origins == script_provider_cached.origins);
+
+ // For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.
for (size_t n = 0; n < spks.size(); ++n) {
BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n].begin(), spks[n].end()));
BOOST_CHECK_EQUAL(IsSolvable(Merge(key_provider, script_provider), spks[n]), (flags & UNSOLVABLE) == 0);
@@ -102,7 +125,19 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
spend.vout.resize(1);
BOOST_CHECK_MESSAGE(SignSignature(Merge(keys_priv, script_provider), spks[n], spend, 0, 1, SIGHASH_ALL), prv);
}
+
+ /* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */
+ auto inferred = InferDescriptor(spks[n], script_provider);
+ BOOST_CHECK_EQUAL(inferred->IsSolvable(), !(flags & UNSOLVABLE));
+ std::vector<CScript> spks_inferred;
+ FlatSigningProvider provider_inferred;
+ BOOST_CHECK(inferred->Expand(0, provider_inferred, spks_inferred, provider_inferred));
+ BOOST_CHECK_EQUAL(spks_inferred.size(), 1);
+ BOOST_CHECK(spks_inferred[0] == spks[n]);
+ BOOST_CHECK_EQUAL(IsSolvable(provider_inferred, spks_inferred[0]), !(flags & UNSOLVABLE));
+ BOOST_CHECK(provider_inferred.origins == script_provider.origins);
}
+
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
// and then remove it from that set.
for (const auto& origin : script_provider.origins) {
@@ -111,6 +146,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
}
}
}
+
// Verify no expected paths remain that were not observed.
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 0432ede3e0..14ddf4d10e 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -28,7 +28,7 @@ static void ResetArgs(const std::string& strArg)
vecChar.push_back(s.c_str());
std::string error;
- gArgs.ParseParameters(vecChar.size(), vecChar.data(), error);
+ BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}
static void SetupArgs(const std::vector<std::string>& args)
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 213afed730..c1ee231d8a 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -189,4 +189,42 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode2->fFeeler == false);
}
+// prior to PR #14728, this test triggers an undefined behavior
+BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
+{
+ // set up local addresses; all that's necessary to reproduce the bug is
+ // that a normal IPv4 address is among the entries, but if this address is
+ // !IsRoutable the undefined behavior is easier to trigger deterministically
+ {
+ LOCK(cs_mapLocalHost);
+ in_addr ipv4AddrLocal;
+ ipv4AddrLocal.s_addr = 0x0100007f;
+ CNetAddr addr = CNetAddr(ipv4AddrLocal);
+ LocalServiceInfo lsi;
+ lsi.nScore = 23;
+ lsi.nPort = 42;
+ mapLocalHost[addr] = lsi;
+ }
+
+ // create a peer with an IPv4 address
+ in_addr ipv4AddrPeer;
+ ipv4AddrPeer.s_addr = 0xa0b0c001;
+ CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
+ std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, false);
+ pnode->fSuccessfullyConnected.store(true);
+
+ // the peer claims to be reaching us via IPv6
+ in6_addr ipv6AddrLocal;
+ memset(ipv6AddrLocal.s6_addr, 0, 16);
+ ipv6AddrLocal.s6_addr[0] = 0xcc;
+ CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
+ pnode->SetAddrLocal(addrLocal);
+
+ // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
+ AdvertiseLocal(&*pnode);
+
+ // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
+ BOOST_CHECK(1);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 121b72a5f7..ff48398925 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -129,9 +129,6 @@ BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
{
BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\"}"));
- // Allow more than one data transaction output
- BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\",\"data\":\"68656c6c6f776f726c64\"}"));
-
// Key not "data" (bad address)
BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"somedata\":\"68656c6c6f776f726c64\"}"), std::runtime_error);
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index e754996d2f..002f61f6a2 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(varints)
}
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
- uint64_t j = -1;
+ uint64_t j = std::numeric_limits<uint64_t>::max();
ss >> VARINT(j);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index c329844341..773204a00c 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -105,7 +105,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
txin.prevout.hash = InsecureRand256();
txin.prevout.n = InsecureRandBits(2);
RandomScript(txin.scriptSig);
- txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : (unsigned int)-1;
+ txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : std::numeric_limits<uint32_t>::max();
}
for (int out = 0; out < outs; out++) {
tx.vout.push_back(CTxOut());
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index ff19b12a9c..9acebdd820 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -187,7 +187,7 @@ struct TestArgsManager : public ArgsManager
m_config_args.clear();
}
std::string error;
- ReadConfigStream(streamConfig, error);
+ BOOST_REQUIRE(ReadConfigStream(streamConfig, error));
}
void SetNetworkOnlyArg(const std::string arg)
{
@@ -210,13 +210,13 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
std::string error;
testArgs.SetupArgs(4, avail_args);
- testArgs.ParseParameters(0, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
- testArgs.ParseParameters(1, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
- testArgs.ParseParameters(7, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
@@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error;
testArgs.SetupArgs(6, avail_args);
- testArgs.ParseParameters(7, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// Each letter should be set.
for (const char opt : "abcdef")
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
testArgs.SetupArgs(2, avail_args);
std::string error;
- testArgs.ParseParameters(4, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(4, (char**)argv_test, error));
// This was passed twice, second one overrides the negative setting.
BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
@@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
// Config test
const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n";
- testArgs.ParseParameters(1, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
testArgs.ReadConfigString(conf_test);
// This was passed twice, second one overrides the negative setting,
@@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
// Combined test
const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"};
const char *combo_test_conf = "foo=1\nnobar=1\n";
- testArgs.ParseParameters(3, (char**)combo_test_args, error);
+ BOOST_CHECK(testArgs.ParseParameters(3, (char**)combo_test_args, error));
testArgs.ReadConfigString(combo_test_conf);
// Command line overrides, but doesn't erase old setting
@@ -557,38 +557,38 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1";
std::string error;
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "main");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest");
- test_args.ParseParameters(3, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(3, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
@@ -596,23 +596,23 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
// [test] regtest=1 potentially relevant) doesn't break things
test_args.SelectConfigNetwork("test");
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(2, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
}
diff --git a/src/uint256.h b/src/uint256.h
index 26a3331d92..97e0cfa015 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -12,7 +12,6 @@
#include <stdint.h>
#include <string>
#include <vector>
-#include <crypto/common.h>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
@@ -123,16 +122,6 @@ class uint256 : public base_blob<256> {
public:
uint256() {}
explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
-
- /** A cheap hash function that just returns 64 bits from the result, it can be
- * used when the contents are considered uniformly random. It is not appropriate
- * when the value can easily be influenced from outside as e.g. a network adversary could
- * provide values to trigger worst-case behavior.
- */
- uint64_t GetCheapHash() const
- {
- return ReadLE64(data);
- }
};
/* uint256 from const char *.
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index 9133f46d5d..b8e2812a96 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -9,16 +9,17 @@
#ifndef BITCOIN_UTIL_MONEYSTR_H
#define BITCOIN_UTIL_MONEYSTR_H
-#include <stdint.h>
-#include <string>
-
#include <amount.h>
+#include <attributes.h>
+
+#include <cstdint>
+#include <string>
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
std::string FormatMoney(const CAmount& n);
-bool ParseMoney(const std::string& str, CAmount& nRet);
-bool ParseMoney(const char* pszIn, CAmount& nRet);
+NODISCARD bool ParseMoney(const std::string& str, CAmount& nRet);
+NODISCARD bool ParseMoney(const char* pszIn, CAmount& nRet);
#endif // BITCOIN_UTIL_MONEYSTR_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 2a2df43337..46146be66f 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -263,7 +263,7 @@ std::string DecodeBase32(const std::string& str)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-static bool ParsePrechecks(const std::string& str)
+NODISCARD static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 87ccf40a1b..7d16d7dcfd 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -9,7 +9,9 @@
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
-#include <stdint.h>
+#include <attributes.h>
+
+#include <cstdint>
#include <string>
#include <vector>
@@ -92,35 +94,35 @@ constexpr inline bool IsSpace(char c) noexcept {
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseInt32(const std::string& str, int32_t *out);
+NODISCARD bool ParseInt32(const std::string& str, int32_t *out);
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseInt64(const std::string& str, int64_t *out);
+NODISCARD bool ParseInt64(const std::string& str, int64_t *out);
/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseUInt32(const std::string& str, uint32_t *out);
+NODISCARD bool ParseUInt32(const std::string& str, uint32_t *out);
/**
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseUInt64(const std::string& str, uint64_t *out);
+NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out);
/**
* Convert string to double with strict parse error feedback.
* @returns true if the entire string could be parsed as valid double,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseDouble(const std::string& str, double *out);
+NODISCARD bool ParseDouble(const std::string& str, double *out);
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
@@ -173,7 +175,7 @@ bool TimingResistantEqual(const T& a, const T& b)
* @returns true on success, false on error.
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
*/
-bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
+NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
@@ -200,7 +202,7 @@ bool ConvertBits(const O& outfn, I it, I end) {
}
/** Parse an HD keypaths like "m/7/0'/2000". */
-bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
+NODISCARD bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
/**
* Converts the given character to its lowercase equivalent.
diff --git a/src/util/system.cpp b/src/util/system.cpp
index f6f36c2238..8e201ec590 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -73,7 +73,6 @@
#include <malloc.h>
#endif
-#include <boost/thread.hpp>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
@@ -372,15 +371,17 @@ ArgsManager::ArgsManager() :
// nothing to do
}
-void ArgsManager::WarnForSectionOnlyArgs()
+const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
{
+ std::set<std::string> unsuitables;
+
LOCK(cs_args);
// if there's no section selected, don't worry
- if (m_network.empty()) return;
+ if (m_network.empty()) return std::set<std::string> {};
// if it's okay to use the default section for this network, don't worry
- if (m_network == CBaseChainParams::MAIN) return;
+ if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
for (const auto& arg : m_network_only_args) {
std::pair<bool, std::string> found_result;
@@ -398,8 +399,28 @@ void ArgsManager::WarnForSectionOnlyArgs()
if (!found_result.first) continue;
// otherwise, issue a warning
- LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network);
+ unsuitables.insert(arg);
}
+ return unsuitables;
+}
+
+
+const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
+{
+ // Section names to be recognized in the config file.
+ static const std::set<std::string> available_sections{
+ CBaseChainParams::REGTEST,
+ CBaseChainParams::TESTNET,
+ CBaseChainParams::MAIN
+ };
+ std::set<std::string> diff;
+
+ LOCK(cs_args);
+ std::set_difference(
+ m_config_sections.begin(), m_config_sections.end(),
+ available_sections.begin(), available_sections.end(),
+ std::inserter(diff, diff.end()));
+ return diff;
}
void ArgsManager::SelectConfigNetwork(const std::string& network)
@@ -820,7 +841,7 @@ static std::string TrimString(const std::string& str, const std::string& pattern
return str.substr(front, end - front + 1);
}
-static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>> &options)
+static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::set<std::string>& sections)
{
std::string str, prefix;
std::string::size_type pos;
@@ -835,7 +856,9 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
str = TrimString(str, pattern);
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
- prefix = str.substr(1, str.size() - 2) + '.';
+ const std::string section = str.substr(1, str.size() - 2);
+ sections.insert(section);
+ prefix = section + '.';
} else if (*str.begin() == '-') {
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
return false;
@@ -847,6 +870,9 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return false;
}
options.emplace_back(name, value);
+ if ((pos = name.rfind('.')) != std::string::npos) {
+ sections.insert(name.substr(0, pos));
+ }
} else {
error = strprintf("parse error on line %i: %s", linenr, str);
if (str.size() >= 2 && str.substr(0, 2) == "no") {
@@ -864,7 +890,8 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, boo
{
LOCK(cs_args);
std::vector<std::pair<std::string, std::string>> options;
- if (!GetConfigOptions(stream, error, options)) {
+ m_config_sections.clear();
+ if (!GetConfigOptions(stream, error, options, m_config_sections)) {
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
diff --git a/src/util/system.h b/src/util/system.h
index 5634b8dd61..dca32cc6fc 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -14,6 +14,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <attributes.h>
#include <compat.h>
#include <fs.h>
#include <logging.h>
@@ -148,8 +149,9 @@ protected:
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
+ std::set<std::string> m_config_sections GUARDED_BY(cs_args);
- bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
public:
ArgsManager();
@@ -159,8 +161,8 @@ public:
*/
void SelectConfigNetwork(const std::string& network);
- bool ParseParameters(int argc, const char* const argv[], std::string& error);
- bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ParseParameters(int argc, const char* const argv[], std::string& error);
+ NODISCARD bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/**
* Log warnings for options in m_section_only_args when
@@ -168,7 +170,12 @@ public:
* on the command line or in a network-specific section in the
* config file.
*/
- void WarnForSectionOnlyArgs();
+ const std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
+
+ /**
+ * Log warnings for unrecognized section names in the config file.
+ */
+ const std::set<std::string> GetUnrecognizedSections() const;
/**
* Return a vector of strings of the given argument
diff --git a/src/validation.cpp b/src/validation.cpp
index 241957878e..e92cbf1b8d 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1680,8 +1680,7 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
-// Protected by cs_main
-VersionBitsCache versionbitscache;
+VersionBitsCache versionbitscache GUARDED_BY(cs_main);
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
@@ -1722,8 +1721,7 @@ public:
}
};
-// Protected by cs_main
-static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
// mainnet. We no longer need to support disabling the segwit deployment
@@ -2489,7 +2487,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
- assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
+ assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
@@ -2779,7 +2777,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
// call preciousblock 2**31-1 times on the same set of tips...
nBlockReverseSequenceId--;
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
}
@@ -2840,7 +2838,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
it++;
@@ -2870,7 +2868,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
@@ -2948,7 +2946,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
setDirtyBlockIndex.insert(pindexNew);
- if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) {
+ if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
std::deque<CBlockIndex*> queue;
queue.push_back(pindexNew);
@@ -3532,12 +3530,14 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
CBlockIndex *pindex = nullptr;
if (fNewBlock) *fNewBlock = false;
CValidationState state;
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
- bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
+ // Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
+ // Ensure that CheckBlock() passes before calling AcceptBlock, as
+ // belt-and-suspenders.
+ bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
@@ -3819,8 +3819,6 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
return false;
- boost::this_thread::interruption_point();
-
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
@@ -3839,7 +3837,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
// Pruned nodes may have deleted the block.
if (pindex->nTx > 0) {
if (pindex->pprev) {
- if (pindex->pprev->nChainTx) {
+ if (pindex->pprev->HaveTxsDownloaded()) {
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
} else {
pindex->nChainTx = 0;
@@ -3853,7 +3851,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex;
@@ -4222,7 +4220,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
++ret.first;
}
}
- } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) {
+ } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindexIter);
}
}
@@ -4523,7 +4521,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
}
- if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
+ if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
if (!fHavePruned) {
@@ -4536,9 +4534,9 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
}
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
- // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set.
- assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned).
- assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0));
+ // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
+ assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
+ assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
diff --git a/src/validation.h b/src/validation.h
index 3e98ebc866..b5548a9293 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -12,6 +12,7 @@
#include <amount.h>
#include <coins.h>
+#include <crypto/common.h> // for ReadLE64
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <policy/feerate.h>
@@ -138,7 +139,10 @@ static const int DEFAULT_STOPATHEIGHT = 0;
struct BlockHasher
{
- size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
+ // this used to call `GetCheapHash()` in uint256, which was later moved; the
+ // cheap hash function simply calls ReadLE64() however, so the end result is
+ // identical
+ size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
extern CScript COINBASE_FLAGS;
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index de320b4e9e..f5ac6e98b2 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -202,7 +202,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
- assert(false);
+ throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
}
if (keyFail || !keyPass)
return false;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 74787eb5d2..98e2abbd18 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -56,9 +56,8 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
return memcmp(value, &rhs.value, sizeof(value)) == 0;
}
-BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
{
- fs::path env_directory;
if (fs::is_regular_file(wallet_path)) {
// Special case for backwards compatibility: if wallet path points to an
// existing file, treat it as the path to a BDB data file in a parent
@@ -71,6 +70,23 @@ BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& data
env_directory = wallet_path;
database_filename = "wallet.dat";
}
+}
+
+bool IsWalletLoaded(const fs::path& wallet_path)
+{
+ fs::path env_directory;
+ std::string database_filename;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ LOCK(cs_db);
+ auto env = g_dbenvs.find(env_directory.string());
+ if (env == g_dbenvs.end()) return false;
+ return env->second.IsDatabaseLoaded(database_filename);
+}
+
+BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+{
+ fs::path env_directory;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
LOCK(cs_db);
// Note: An unused temporary BerkeleyEnvironment object may be created inside the
// emplace function if the key already exists. This is a little inefficient,
@@ -90,13 +106,13 @@ void BerkeleyEnvironment::Close()
fDbEnvInit = false;
- for (auto& db : mapDb) {
+ for (auto& db : m_databases) {
auto count = mapFileUseCount.find(db.first);
assert(count == mapFileUseCount.end() || count->second == 0);
- if (db.second) {
- db.second->close(0);
- delete db.second;
- db.second = nullptr;
+ BerkeleyDatabase& database = db.second.get();
+ if (database.m_db) {
+ database.m_db->close(0);
+ database.m_db.reset();
}
}
@@ -463,7 +479,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
if (!env->Open(false /* retry */))
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
- pdb = env->mapDb[strFilename];
+ pdb = database.m_db.get();
if (pdb == nullptr) {
int ret;
std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
@@ -508,7 +524,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
}
pdb = pdb_temp.release();
- env->mapDb[strFilename] = pdb;
+ database.m_db.reset(pdb);
if (fCreate && !Exists(std::string("version"))) {
bool fTmp = fReadOnly;
@@ -563,12 +579,13 @@ void BerkeleyEnvironment::CloseDb(const std::string& strFile)
{
{
LOCK(cs_db);
- if (mapDb[strFile] != nullptr) {
+ auto it = m_databases.find(strFile);
+ assert(it != m_databases.end());
+ BerkeleyDatabase& database = it->second.get();
+ if (database.m_db) {
// Close the database handle
- Db* pdb = mapDb[strFile];
- pdb->close(0);
- delete pdb;
- mapDb[strFile] = nullptr;
+ database.m_db->close(0);
+ database.m_db.reset();
}
}
}
@@ -586,7 +603,7 @@ void BerkeleyEnvironment::ReloadDbEnv()
});
std::vector<std::string> filenames;
- for (auto it : mapDb) {
+ for (auto it : m_databases) {
filenames.push_back(it.first);
}
// Close the individual Db's
@@ -692,7 +709,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
{
int64_t nStart = GetTimeMillis();
// Flush log data to the actual data file on all files that are not in use
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
+ LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
if (!fDbEnvInit)
return;
{
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 8f96483a18..e453d441d7 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -31,6 +31,8 @@ struct WalletDatabaseFileId {
bool operator==(const WalletDatabaseFileId& rhs) const;
};
+class BerkeleyDatabase;
+
class BerkeleyEnvironment
{
private:
@@ -43,7 +45,7 @@ private:
public:
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
- std::map<std::string, Db*> mapDb;
+ std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
std::condition_variable_any m_db_in_use;
@@ -54,6 +56,7 @@ public:
void MakeMock();
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
+ bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; }
/**
@@ -95,6 +98,9 @@ public:
}
};
+/** Return whether a wallet database is currently loaded. */
+bool IsWalletLoaded(const fs::path& wallet_path);
+
/** Get BerkeleyEnvironment and database filename given a wallet path. */
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
@@ -115,6 +121,8 @@ public:
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
{
env = GetWalletEnv(wallet_path, strFile);
+ auto inserted = env->m_databases.emplace(strFile, std::ref(*this));
+ assert(inserted.second);
if (mock) {
env->Close();
env->Reset();
@@ -122,6 +130,13 @@ public:
}
}
+ ~BerkeleyDatabase() {
+ if (env) {
+ size_t erased = env->m_databases.erase(strFile);
+ assert(erased == 1);
+ }
+ }
+
/** Return object for accessing database at specified path. */
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
{
@@ -161,6 +176,9 @@ public:
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
+ /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
+ std::unique_ptr<Db> m_db;
+
private:
/** BerkeleyDB specific */
BerkeleyEnvironment *env;
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e1e4fc51fe..647af3eb86 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -3,18 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
+#include <core_io.h>
#include <interfaces/chain.h>
#include <key_io.h>
+#include <merkleblock.h>
#include <rpc/server.h>
-#include <validation.h>
+#include <rpc/util.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
#include <util/system.h>
#include <util/time.h>
+#include <validation.h>
#include <wallet/wallet.h>
-#include <merkleblock.h>
-#include <core_io.h>
#include <wallet/rpcwallet.h>
@@ -108,13 +109,15 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
- "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
- "Hint: use importmulti to import more than one private key.\n"
- "\nArguments:\n"
- "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
- "2. \"label\" (string, optional, default=\"\") An optional label\n"
- "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ RPCHelpMan{"importprivkey",
+ "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
+ "Hint: use importmulti to import more than one private key.\n",
+ {
+ {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key (see dumpprivkey)"},
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "current label if address exists, otherwise \"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ }}
+ .ToString() +
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"\nExamples:\n"
@@ -163,9 +166,14 @@ UniValue importprivkey(const JSONRPCRequest& request)
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
- // We don't know which corresponding address will be used; label them all
+
+ // We don't know which corresponding address will be used;
+ // label all new addresses, and label existing addresses if a
+ // label was passed.
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
- pwallet->SetAddressBook(dest, strLabel, "receive");
+ if (!request.params[1].isNull() || pwallet->mapAddressBook.count(dest) == 0) {
+ pwallet->SetAddressBook(dest, strLabel, "receive");
+ }
}
// Don't throw error in case a key is already there
@@ -200,8 +208,9 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "abortrescan\n"
- "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
+ RPCHelpMan{"abortrescan",
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n", {}}
+ .ToString() +
"\nExamples:\n"
"\nImport a private key\n"
+ HelpExampleCli("importprivkey", "\"mykey\"") +
@@ -262,13 +271,15 @@ UniValue importaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
- "importaddress \"address\" ( \"label\" rescan p2sh )\n"
- "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The Bitcoin address (or hex-encoded script)\n"
- "2. \"label\" (string, optional, default=\"\") An optional label\n"
- "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
- "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
+ RPCHelpMan{"importaddress",
+ "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The Bitcoin address (or hex-encoded script)"},
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ {"p2sh", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Add the P2SH version of the script as well"},
+ }}
+ .ToString() +
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
@@ -342,11 +353,13 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "importprunedfunds\n"
- "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n"
- "\nArguments:\n"
- "1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
- "2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
+ RPCHelpMan{"importprunedfunds",
+ "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
+ {
+ {"rawtransaction", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A raw transaction in hex funding an already-existing address in wallet"},
+ {"txoutproof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex output from gettxoutproof that contains the transaction"},
+ }}
+ .ToString()
);
CMutableTransaction tx;
@@ -406,10 +419,12 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "removeprunedfunds \"txid\"\n"
- "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
+ RPCHelpMan{"removeprunedfunds",
+ "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded id of the transaction you are deleting"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
"\nAs a JSON-RPC call\n"
@@ -445,12 +460,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "importpubkey \"pubkey\" ( \"label\" rescan )\n"
- "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
- "\nArguments:\n"
- "1. \"pubkey\" (string, required) The hex-encoded public key\n"
- "2. \"label\" (string, optional, default=\"\") An optional label\n"
- "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ RPCHelpMan{"importpubkey",
+ "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n",
+ {
+ {"pubkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ }}
+ .ToString() +
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"\nExamples:\n"
@@ -517,10 +534,12 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "importwallet \"filename\"\n"
- "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
- "\nArguments:\n"
- "1. \"filename\" (string, required) The wallet file\n"
+ RPCHelpMan{"importwallet",
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
+ {
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet file"},
+ }}
+ .ToString() +
"\nExamples:\n"
"\nDump the wallet\n"
+ HelpExampleCli("dumpwallet", "\"test\"") +
@@ -647,11 +666,13 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "dumpprivkey \"address\"\n"
- "\nReveals the private key corresponding to 'address'.\n"
- "Then the importprivkey can be used with this output\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address for the private key\n"
+ RPCHelpMan{"dumpprivkey",
+ "\nReveals the private key corresponding to 'address'.\n"
+ "Then the importprivkey can be used with this output\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for the private key"},
+ }}
+ .ToString() +
"\nResult:\n"
"\"key\" (string) The private key\n"
"\nExamples:\n"
@@ -692,13 +713,15 @@ UniValue dumpwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "dumpwallet \"filename\"\n"
- "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
- "Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
- "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
- "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
- "\nArguments:\n"
- "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
+ RPCHelpMan{"dumpwallet",
+ "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
+ "Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
+ "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
+ "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
+ {
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The filename with path (either absolute or relative to bitcoind)"},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object)\n"
" \"filename\" : { (string) The filename with full absolute path\n"
@@ -1003,8 +1026,9 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet");
}
- // add to address book or update label
- if (IsValidDestination(scriptpubkey_dest)) {
+ // if not internal add to address book or update label
+ if (!internal) {
+ assert(IsValidDestination(scriptpubkey_dest));
pwallet->SetAddressBook(scriptpubkey_dest, label, "receive");
}
@@ -1076,36 +1100,52 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
return NullUniValue;
}
- // clang-format off
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
- "importmulti \"requests\" ( \"options\" )\n\n"
- "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
- "Arguments:\n"
- "1. requests (array, required) Data to be imported\n"
- " [ (array of json objects)\n"
- " {\n"
- " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
- " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
- " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
- " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
- " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
- " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
- " creation time of all keys being imported by the importmulti call will be scanned.\n"
- " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey\n"
- " \"witnessscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey\n"
- " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
- " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
- " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
- " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
- " \"label\": <label> , (string, optional, default: '') Label to assign to the address, only allowed with internal=false\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "2. options (json, optional)\n"
- " {\n"
- " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
- " }\n"
+ RPCHelpMan{"importmulti",
+ "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n",
+ {
+ {"requests", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Data to be imported",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {
+ {"scriptPubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Type of scriptPubKey (string for script, json for address)",
+ /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
+ },
+ {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
+ " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
+ " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
+ " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
+ " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
+ " creation time of all keys being imported by the importmulti call will be scanned.",
+ /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
+ },
+ {"redeemscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
+ {"witnessscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
+ {"pubkeys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving pubkeys that must occur in the output or redeemscript",
+ {
+ {"pubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ }
+ },
+ {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript",
+ {
+ {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ }
+ },
+ {"internal", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be treated as not incoming payments aka change"},
+ {"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty"},
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "''", "Label to assign to the address, only allowed with internal=false"},
+ },
+ },
+ },
+ "\"requests\""},
+ {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {
+ {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Stating if should rescan the blockchain after all imports"},
+ },
+ "\"options\""},
+ }}
+ .ToString() +
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n"
"\nExamples:\n" +
@@ -1116,7 +1156,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"\nResponse is an array with the same size as the input that has the execution result :\n"
" [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
- // clang-format on
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 3c92eb3120..1d9ab54857 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -22,6 +22,7 @@
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <script/sign.h>
#include <shutdown.h>
#include <timedata.h>
@@ -149,13 +150,15 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getnewaddress ( \"label\" \"address_type\" )\n"
- "\nReturns a new Bitcoin address for receiving payments.\n"
- "If 'label' is specified, it is added to the address book \n"
- "so payments received with the address will be associated with 'label'.\n"
- "\nArguments:\n"
- "1. \"label\" (string, optional) The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
- "2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
+ RPCHelpMan{"getnewaddress",
+ "\nReturns a new Bitcoin address for receiving payments.\n"
+ "If 'label' is specified, it is added to the address book \n"
+ "so payments received with the address will be associated with 'label'.\n",
+ {
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
+ {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ }}
+ .ToString() +
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n"
@@ -209,11 +212,13 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getrawchangeaddress ( \"address_type\" )\n"
- "\nReturns a new Bitcoin address, for receiving change.\n"
- "This is for use with raw transactions, NOT normal use.\n"
- "\nArguments:\n"
- "1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
+ RPCHelpMan{"getrawchangeaddress",
+ "\nReturns a new Bitcoin address, for receiving change.\n"
+ "This is for use with raw transactions, NOT normal use.\n",
+ {
+ {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ }}
+ .ToString() +
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n"
@@ -263,11 +268,13 @@ static UniValue setlabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "setlabel \"address\" \"label\"\n"
- "\nSets the label associated with the given address.\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to be associated with a label.\n"
- "2. \"label\" (string, required) The label to assign to the address.\n"
+ RPCHelpMan{"setlabel",
+ "\nSets the label associated with the given address.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to be associated with a label."},
+ {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label to assign to the address."},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")
@@ -343,25 +350,27 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n"
- "\nSend an amount to a given address.\n"
- + HelpRequiringPassphrase(pwallet) +
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to send to.\n"
- "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n"
- "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
- " This is not part of the transaction, just kept in your wallet.\n"
- "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n"
+ RPCHelpMan{"sendtoaddress",
+ "\nSend an amount to a given address." +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to send to."},
+ {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
+ {"comment", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment used to store what the transaction is for.\n"
+ " This is not part of the transaction, just kept in your wallet."},
+ {"comment_to", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment to store the name of the person or organization\n"
" to which you're sending the transaction. This is not part of the \n"
- " transaction, just kept in your wallet.\n"
- "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
- " The recipient will receive less bitcoins than you enter in the amount field.\n"
- "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
- "7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
- "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " transaction, just kept in your wallet."},
+ {"subtractfeefromamount", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "The fee will be deducted from the amount being sent.\n"
+ " The recipient will receive less bitcoins than you enter in the amount field."},
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
+ " \"CONSERVATIVE\""},
+ }}
+ .ToString() +
"\nResult:\n"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n"
@@ -433,10 +442,12 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listaddressgroupings\n"
- "\nLists groups of addresses which have had their common ownership\n"
- "made public by common use as inputs or as the resulting change\n"
- "in past transactions\n"
+ RPCHelpMan{"listaddressgroupings",
+ "\nLists groups of addresses which have had their common ownership\n"
+ "made public by common use as inputs or as the resulting change\n"
+ "in past transactions\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" [\n"
@@ -493,12 +504,14 @@ static UniValue signmessage(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "signmessage \"address\" \"message\"\n"
- "\nSign a message with the private key of an address"
- + HelpRequiringPassphrase(pwallet) + "\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to use for the private key.\n"
- "2. \"message\" (string, required) The message to create a signature of.\n"
+ RPCHelpMan{"signmessage",
+ "\nSign a message with the private key of an address" +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the private key."},
+ {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."},
+ }}
+ .ToString() +
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
"\nExamples:\n"
@@ -557,11 +570,13 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getreceivedbyaddress \"address\" ( minconf )\n"
- "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address for transactions.\n"
- "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
+ RPCHelpMan{"getreceivedbyaddress",
+ "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for transactions."},
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."},
+ }}
+ .ToString() +
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
"\nExamples:\n"
@@ -626,11 +641,13 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getreceivedbylabel \"label\" ( minconf )\n"
- "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n"
- "\nArguments:\n"
- "1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
- "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
+ RPCHelpMan{"getreceivedbylabel",
+ "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
+ {
+ {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The selected label, may be the default label using \"\"."},
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."},
+ }}
+ .ToString() +
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
"\nExamples:\n"
@@ -693,14 +710,16 @@ static UniValue getbalance(const JSONRPCRequest& request)
if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
- "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
- "\nReturns the total available balance.\n"
- "The available balance is what the wallet considers currently spendable, and is\n"
- "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
- "\nArguments:\n"
- "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
- "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
- "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
+ RPCHelpMan{"getbalance",
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
+ {
+ {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Remains for backward compatibility. Must be excluded or set to \"*\"."},
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Only include transactions confirmed at least this many times."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
+ }}
+ .ToString() +
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
"\nExamples:\n"
@@ -748,8 +767,9 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "getunconfirmedbalance\n"
- "Returns the server's total unconfirmed balance\n");
+ RPCHelpMan{"getunconfirmedbalance",
+ "Returns the server's total unconfirmed balance\n", {}}
+ .ToString());
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -773,32 +793,34 @@ static UniValue sendmany(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
- "\nSend multiple times. Amounts are double-precision floating point numbers.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
- "\nArguments:\n"
- "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
- "2. \"amounts\" (string, required) A json object with addresses and amounts\n"
- " {\n"
- " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n"
- " ,...\n"
- " }\n"
- "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
- "4. \"comment\" (string, optional) A comment\n"
- "5. subtractfeefrom (array, optional) A json array with addresses.\n"
+ RPCHelpMan{"sendmany",
+ "\nSend multiple times. Amounts are double-precision floating point numbers." +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"dummy", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Must be set to \"\" for backwards compatibility.", "\"\""},
+ {"amounts", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object with addresses and amounts",
+ {
+ {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
+ },
+ },
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only use the balance confirmed at least this many times."},
+ {"comment", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment"},
+ {"subtractfeefrom", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no addresses are specified here, the sender pays the fee.\n"
- " [\n"
- " \"address\" (string) Subtract fee from this address\n"
- " ,...\n"
- " ]\n"
- "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
- "7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
- "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " If no addresses are specified here, the sender pays the fee.",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Subtract fee from this address"},
+ },
+ },
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
+ " \"CONSERVATIVE\""},
+ }}
+ .ToString() +
"\nResult:\n"
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
@@ -926,23 +948,24 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
}
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"label\" \"address_type\" )\n"
- "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
- "Each key is a Bitcoin address or hex-encoded public key.\n"
- "This functionality is only intended for use with non-watchonly addresses.\n"
- "See `importaddress` for watchonly p2sh address support.\n"
- "If 'label' is specified, assign address to that label.\n"
-
- "\nArguments:\n"
- "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
- "2. \"keys\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n"
- " [\n"
- " \"address\" (string) bitcoin address or hex-encoded public key\n"
- " ...,\n"
- " ]\n"
- "3. \"label\" (string, optional) A label to assign the addresses to.\n"
- "4. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
-
+ std::string msg =
+ RPCHelpMan{"addmultisigaddress",
+ "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
+ "Each key is a Bitcoin address or hex-encoded public key.\n"
+ "This functionality is only intended for use with non-watchonly addresses.\n"
+ "See `importaddress` for watchonly p2sh address support.\n"
+ "If 'label' is specified, assign address to that label.\n",
+ {
+ {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys or addresses."},
+ {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of bitcoin addresses or hex-encoded public keys",
+ {
+ {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "bitcoin address or hex-encoded public key"},
+ },
+ },
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A label to assign the addresses to."},
+ {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
@@ -1166,13 +1189,15 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n"
- "\nList balances by receiving address.\n"
- "\nArguments:\n"
- "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
- "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
- "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
- "4. address_filter (string, optional) If present, only return information on this address.\n"
+ RPCHelpMan{"listreceivedbyaddress",
+ "\nList balances by receiving address.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include addresses that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"address_filter", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If present, only return information on this address."},
+ }}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -1217,13 +1242,14 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw std::runtime_error(
- "listreceivedbylabel ( minconf include_empty include_watchonly)\n"
- "\nList received transactions by label.\n"
- "\nArguments:\n"
- "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
- "2. include_empty (bool, optional, default=false) Whether to include labels that haven't received any payments.\n"
- "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
-
+ RPCHelpMan{"listreceivedbylabel",
+ "\nList received transactions by label.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include labels that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ }}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -1261,25 +1287,26 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
/**
* List transactions based on the given criteria.
*
- * @param pwallet The wallet.
- * @param wtx The wallet transaction.
- * @param nMinDepth The minimum confirmation depth.
- * @param fLong Whether to include the JSON version of the transaction.
- * @param ret The UniValue into which the result is stored.
- * @param filter The "is mine" filter bool.
+ * @param pwallet The wallet.
+ * @param wtx The wallet transaction.
+ * @param nMinDepth The minimum confirmation depth.
+ * @param fLong Whether to include the JSON version of the transaction.
+ * @param ret The UniValue into which the result is stored.
+ * @param filter_ismine The "is mine" filter flags.
+ * @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
- wtx.GetAmounts(listReceived, listSent, nFee, filter);
+ wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
- if ((!listSent.empty() || nFee != 0))
+ if (!filter_label)
{
for (const COutputEntry& s : listSent)
{
@@ -1311,6 +1338,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
if (pwallet->mapAddressBook.count(r.destination)) {
label = pwallet->mapAddressBook[r.destination].name;
}
+ if (filter_label && label != *filter_label) {
+ continue;
+ }
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
@@ -1352,13 +1382,17 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listtransactions (dummy count skip include_watchonly)\n"
- "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n"
- "\nArguments:\n"
- "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n"
- "2. count (numeric, optional, default=10) The number of transactions to return\n"
- "3. skip (numeric, optional, default=0) The number of transactions to skip\n"
- "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
+ RPCHelpMan{"listtransactions",
+ "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
+ "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
+ {
+ {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If set, should be a valid label name to return only incoming transactions\n"
+ " with the specified label, or \"*\" to disable filtering and return all transactions."},
+ {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "10", "The number of transactions to return"},
+ {"skip", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "The number of transactions to skip"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ }}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -1400,8 +1434,12 @@ UniValue listtransactions(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
+ const std::string* filter_label = nullptr;
if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\"");
+ filter_label = &request.params[0].get_str();
+ if (filter_label->empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
+ }
}
int nCount = 10;
if (!request.params[1].isNull())
@@ -1431,7 +1469,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter);
+ ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1473,16 +1511,18 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listsinceblock ( \"blockhash\" target_confirmations include_watchonly include_removed )\n"
- "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
- "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
- "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n"
- "\nArguments:\n"
- "1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
- "2. target_confirmations: (numeric, optional, 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\n"
- "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
- "4. include_removed: (bool, optional, default=true) Show transactions that were removed due to a reorg in the \"removed\" array\n"
- " (not guaranteed to work on pruned nodes)\n"
+ RPCHelpMan{"listsinceblock",
+ "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
+ "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
+ "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
+ {
+ {"blockhash", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If set, the block hash to list transactions since, otherwise list all transactions."},
+ {"target_confirmations", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "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, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_removed", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
+ " (not guaranteed to work on pruned nodes)"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
@@ -1568,7 +1608,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
- ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter);
+ ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1585,7 +1625,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (it != pwallet->mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter);
+ ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
}
}
paltindex = paltindex->pprev;
@@ -1613,11 +1653,13 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "gettransaction \"txid\" ( include_watchonly )\n"
- "\nGet detailed information about in-wallet transaction <txid>\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
- "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n"
+ RPCHelpMan{"gettransaction",
+ "\nGet detailed information about in-wallet transaction <txid>\n",
+ {
+ {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses in balance calculation and details[]"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n"
@@ -1688,7 +1730,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter);
+ ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
@@ -1708,14 +1750,16 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "abandontransaction \"txid\"\n"
- "\nMark in-wallet transaction <txid> as abandoned\n"
- "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
- "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
- "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
- "It has no effect on transactions which are already abandoned.\n"
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
+ RPCHelpMan{"abandontransaction",
+ "\nMark in-wallet transaction <txid> as abandoned\n"
+ "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
+ "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
+ "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
+ "It has no effect on transactions which are already abandoned.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ }}
+ .ToString() +
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
@@ -1754,10 +1798,12 @@ static UniValue backupwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "backupwallet \"destination\"\n"
- "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n"
- "\nArguments:\n"
- "1. \"destination\" (string) The destination directory or file\n"
+ RPCHelpMan{"backupwallet",
+ "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
+ {
+ {"destination", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The destination directory or file"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
@@ -1790,11 +1836,13 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "keypoolrefill ( newsize )\n"
- "\nFills the keypool."
- + HelpRequiringPassphrase(pwallet) + "\n"
- "\nArguments\n"
- "1. newsize (numeric, optional, default=100) The new keypool size\n"
+ RPCHelpMan{"keypoolrefill",
+ "\nFills the keypool."+
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"newsize", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "100", "The new keypool size"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
@@ -1837,12 +1885,14 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
- "walletpassphrase \"passphrase\" timeout\n"
- "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
- "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
- "\nArguments:\n"
- "1. \"passphrase\" (string, required) The wallet passphrase\n"
- "2. timeout (numeric, required) The time to keep the decryption key in seconds; capped at 100000000 (~3 years).\n"
+ RPCHelpMan{"walletpassphrase",
+ "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
+ "This is needed prior to performing transactions related to private keys such as sending bitcoins\n",
+ {
+ {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet passphrase"},
+ {"timeout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
+ }}
+ .ToString() +
"\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n"
@@ -1882,16 +1932,13 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
nSleepTime = MAX_SLEEP_TIME;
}
- if (strWalletPass.length() > 0)
- {
- if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
- }
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
+
+ if (!pwallet->Unlock(strWalletPass)) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
}
- else
- throw std::runtime_error(
- "walletpassphrase <passphrase> <timeout>\n"
- "Stores the wallet decryption key in memory for <timeout> seconds.");
pwallet->TopUpKeyPool();
@@ -1924,11 +1971,13 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
- "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
- "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
- "\nArguments:\n"
- "1. \"oldpassphrase\" (string) The current passphrase\n"
- "2. \"newpassphrase\" (string) The new passphrase\n"
+ RPCHelpMan{"walletpassphrasechange",
+ "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
+ {
+ {"oldpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The current passphrase"},
+ {"newpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The new passphrase"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
@@ -1952,10 +2001,9 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
strNewWalletPass.reserve(100);
strNewWalletPass = request.params[1].get_str().c_str();
- if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
- throw std::runtime_error(
- "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
- "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
+ if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
@@ -1976,10 +2024,12 @@ static UniValue walletlock(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "walletlock\n"
- "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
- "After calling this method, you will need to call walletpassphrase again\n"
- "before being able to call any methods which require the wallet to be unlocked.\n"
+ RPCHelpMan{"walletlock",
+ "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
+ "After calling this method, you will need to call walletpassphrase again\n"
+ "before being able to call any methods which require the wallet to be unlocked.\n",
+ {}}
+ .ToString() +
"\nExamples:\n"
"\nSet the passphrase for 2 minutes to perform a transaction\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
@@ -2017,14 +2067,16 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "encryptwallet \"passphrase\"\n"
- "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
- "After this, any calls that interact with private keys such as sending or signing \n"
- "will require the passphrase to be set prior the making these calls.\n"
- "Use the walletpassphrase call for this, and then walletlock call.\n"
- "If the wallet is already encrypted, use the walletpassphrasechange call.\n"
- "\nArguments:\n"
- "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
+ RPCHelpMan{"encryptwallet",
+ "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
+ "After this, any calls that interact with private keys such as sending or signing \n"
+ "will require the passphrase to be set prior the making these calls.\n"
+ "Use the walletpassphrase call for this, and then walletlock call.\n"
+ "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
+ {
+ {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
+ }}
+ .ToString() +
"\nExamples:\n"
"\nEncrypt your wallet\n"
+ HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
@@ -2052,10 +2104,9 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
strWalletPass.reserve(100);
strWalletPass = request.params[0].get_str().c_str();
- if (strWalletPass.length() < 1)
- throw std::runtime_error(
- "encryptwallet <passphrase>\n"
- "Encrypts the wallet with <passphrase>.");
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->EncryptWallet(strWalletPass)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
@@ -2075,25 +2126,28 @@ static UniValue lockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
- "\nUpdates list of temporarily unspendable outputs.\n"
- "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
- "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
- "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
- "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
- "is always cleared (by virtue of process exit) when a node stops or fails.\n"
- "Also see the listunspent call\n"
- "\nArguments:\n"
- "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n"
- "2. \"transactions\" (string, optional) A json array of objects. Each object the txid (string) vout (numeric)\n"
- " [ (json array of json objects)\n"
- " {\n"
- " \"txid\":\"id\", (string) The transaction id\n"
- " \"vout\": n (numeric) The output number\n"
- " }\n"
- " ,...\n"
- " ]\n"
-
+ RPCHelpMan{"lockunspent",
+ "\nUpdates list of temporarily unspendable outputs.\n"
+ "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
+ "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
+ "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
+ "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
+ "is always cleared (by virtue of process exit) when a node stops or fails.\n"
+ "Also see the listunspent call\n",
+ {
+ {"unlock", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "Whether to unlock (true) or lock (false) the specified transactions"},
+ {"transactions", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of objects. Each object the txid (string) vout (numeric).",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ },
+ },
+ },
+ },
+ }}
+ .ToString() +
"\nResult:\n"
"true|false (boolean) Whether the command was successful or not\n"
@@ -2201,9 +2255,11 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "listlockunspent\n"
- "\nReturns list of temporarily unspendable outputs.\n"
- "See the lockunspent call to lock and unlock transactions for spending.\n"
+ RPCHelpMan{"listlockunspent",
+ "\nReturns list of temporarily unspendable outputs.\n"
+ "See the lockunspent call to lock and unlock transactions for spending.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -2255,10 +2311,12 @@ static UniValue settxfee(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) {
throw std::runtime_error(
- "settxfee amount\n"
- "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
- "\nArguments:\n"
- "1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n"
+ RPCHelpMan{"settxfee",
+ "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
+ {
+ {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The transaction fee in " + CURRENCY_UNIT + "/kB"},
+ }}
+ .ToString() +
"\nResult\n"
"true|false (boolean) Returns true if successful\n"
"\nExamples:\n"
@@ -2295,8 +2353,9 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getwalletinfo\n"
- "Returns an object containing various wallet state info.\n"
+ RPCHelpMan{"getwalletinfo",
+ "Returns an object containing various wallet state info.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"walletname\": xxxxx, (string) the wallet name\n"
@@ -2357,8 +2416,9 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "listwalletdir\n"
- "Returns a list of wallets in the wallet directory.\n"
+ RPCHelpMan{"listwalletdir",
+ "Returns a list of wallets in the wallet directory.\n", {}}
+ .ToString() +
"{\n"
" \"wallets\" : [ (json array of objects)\n"
" {\n"
@@ -2389,9 +2449,11 @@ static UniValue listwallets(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listwallets\n"
- "Returns a list of currently loaded wallets.\n"
- "For full information on the wallet, use \"getwalletinfo\"\n"
+ RPCHelpMan{"listwallets",
+ "Returns a list of currently loaded wallets.\n"
+ "For full information on the wallet, use \"getwalletinfo\"\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[ (json array of strings)\n"
" \"walletname\" (string) the wallet name\n"
@@ -2421,12 +2483,14 @@ static UniValue loadwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "loadwallet \"filename\"\n"
- "\nLoads a wallet from a wallet file or directory."
- "\nNote that all wallet command-line options used when starting bitcoind will be"
- "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n"
- "\nArguments:\n"
- "1. \"filename\" (string, required) The wallet directory or .dat file.\n"
+ RPCHelpMan{"loadwallet",
+ "\nLoads a wallet from a wallet file or directory."
+ "\nNote that all wallet command-line options used when starting bitcoind will be"
+ "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n",
+ {
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet directory or .dat file."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n"
@@ -2474,11 +2538,13 @@ static UniValue createwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "createwallet \"wallet_name\" ( disable_private_keys )\n"
- "\nCreates and loads a new wallet.\n"
- "\nArguments:\n"
- "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
- "2. disable_private_keys (boolean, optional, default: false) Disable the possibility of private keys (only watchonlys are possible in this mode).\n"
+ RPCHelpMan{"createwallet",
+ "\nCreates and loads a new wallet.\n",
+ {
+ {"wallet_name", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
+ {"disable_private_keys", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
@@ -2526,11 +2592,13 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
- "unloadwallet ( \"wallet_name\" )\n"
- "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
- "Specifying the wallet name on a wallet endpoint is invalid."
- "\nArguments:\n"
- "1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n"
+ RPCHelpMan{"unloadwallet",
+ "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
+ "Specifying the wallet name on a wallet endpoint is invalid.",
+ {
+ {"wallet_name", RPCArg::Type::STR, /* opt */ true, /* default_val */ "the wallet name from the RPC request", "The name of the wallet to unload."},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("unloadwallet", "wallet_name")
+ HelpExampleRpc("unloadwallet", "wallet_name")
@@ -2582,9 +2650,11 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "resendwallettransactions\n"
- "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
- "Intended only for testing; the wallet code periodically re-broadcasts\n"
+ RPCHelpMan{"resendwallettransactions",
+ "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
+ "Intended only for testing; the wallet code periodically re-broadcasts\n",
+ {}}
+ .ToString() +
"automatically.\n"
"Returns an RPC error if -walletbroadcast is set to false.\n"
"Returns array of transaction ids that were re-broadcast.\n"
@@ -2620,27 +2690,30 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 5)
throw std::runtime_error(
- "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n"
- "\nReturns array of unspent transaction outputs\n"
- "with between minconf and maxconf (inclusive) confirmations.\n"
- "Optionally filter to only include txouts paid to specified addresses.\n"
- "\nArguments:\n"
- "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
- "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
- "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n"
- " [\n"
- " \"address\" (string) bitcoin address\n"
- " ,...\n"
- " ]\n"
- "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
- " See description of \"safe\" attribute below.\n"
- "5. query_options (json, optional) JSON with query options\n"
- " {\n"
- " \"minimumAmount\" (numeric or string, default=0) Minimum value of each UTXO in " + CURRENCY_UNIT + "\n"
- " \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
- " \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
- " \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
- " }\n"
+ RPCHelpMan{"listunspent",
+ "\nReturns array of unspent transaction outputs\n"
+ "with between minconf and maxconf (inclusive) confirmations.\n"
+ "Optionally filter to only include txouts paid to specified addresses.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum confirmations to filter"},
+ {"maxconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "9999999", "The maximum confirmations to filter"},
+ {"addresses", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of bitcoin addresses to filter",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "bitcoin address"},
+ },
+ },
+ {"include_unsafe", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Include outputs that are not safe to spend\n"
+ " See description of \"safe\" attribute below."},
+ {"query_options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "JSON with query options",
+ {
+ {"minimumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "unlimited", "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumCount", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "unlimited", "Maximum number of UTXOs"},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
+ },
+ "query_options"},
+ }}
+ .ToString() +
"\nResult\n"
"[ (array of json object)\n"
" {\n"
@@ -2654,6 +2727,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
+ " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
" \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
" and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
@@ -2772,6 +2846,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
+ if (out.fSolvable) {
+ auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
+ entry.pushKV("desc", descriptor->ToString());
+ }
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
@@ -2906,45 +2984,48 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "fundrawtransaction \"hexstring\" ( options iswitness )\n"
- "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
- "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
- "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
- "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
- "The inputs added will not be signed, use signrawtransaction for that.\n"
- "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
- "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
- "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
- "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
- "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
- "2. options (object, optional)\n"
- " {\n"
- " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
- " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
- " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
- " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
- " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
- " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
- " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
+ RPCHelpMan{"fundrawtransaction",
+ "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
+ "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
+ "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
+ "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
+ "The inputs added will not be signed, use signrawtransaction for that.\n"
+ "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
+ "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
+ "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
+ "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
+ "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"},
+ {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
+ {
+ {"changeAddress", RPCArg::Type::STR, /* opt */ true, /* default_val */ "pool address", "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "random", "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "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, /* opt */ true, /* default_val */ "false", "Also select inputs which are watch only"},
+ {"lockUnspents", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Lock selected unspent outputs"},
+ {"feeRate", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of integers.\n"
" The fee will be equally deducted from the amount of each specified output.\n"
- " The outputs are specified by their zero-based index, before any change output is added.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no outputs are specified here, the sender pays the fee.\n"
- " [vout_index,...]\n"
- " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees\n"
- " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
- " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " If no outputs are specified here, the sender pays the fee.",
+ {
+ {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
+ },
+ },
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees"},
+ {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
- " }\n"
- " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
- "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
- " If iswitness is not present, heuristic tests will be used in decoding\n"
-
+ " \"CONSERVATIVE\""},
+ },
+ "options"},
+ {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction \n"
+ " If iswitness is not present, heuristic tests will be used in decoding"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
@@ -2995,33 +3076,35 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block chain.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
-
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The transaction hex string\n"
- "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
- " [ (json array of json objects, or 'null' if none provided)\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"scriptPubKey\": \"hex\", (string, required) script key\n"
- " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
- " \"amount\": value (numeric, required) The amount spent\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ RPCHelpMan{"signrawtransactionwithwallet",
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain." +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"hexstring", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction hex string"},
+ {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "script key"},
+ {"redeemScript", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "omitted", "(required for P2SH or P2WSH)"},
+ {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount spent"},
+ },
+ },
+ },
+ },
+ {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
- " \"SINGLE|ANYONECANPAY\"\n"
-
+ " \"SINGLE|ANYONECANPAY\""},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
@@ -3069,39 +3152,42 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "bumpfee \"txid\" ( options ) \n"
- "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
- "An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
- "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
- "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
- "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
- "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
- "The user can specify a confirmation target for estimatesmartfee.\n"
- "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
- "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
- "returned by getnetworkinfo) to enter the node's mempool.\n"
- "\nArguments:\n"
- "1. txid (string, required) The txid to be bumped\n"
- "2. options (object, optional)\n"
- " {\n"
- " \"confTarget\" (numeric, optional) Confirmation target (in blocks)\n"
- " \"totalFee\" (numeric, optional) Total fee (NOT feerate) to pay, in satoshis.\n"
+ RPCHelpMan{"bumpfee",
+ "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
+ "An opt-in RBF transaction with the given txid must be in the wallet.\n"
+ "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
+ "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
+ "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
+ "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
+ "The user can specify a confirmation target for estimatesmartfee.\n"
+ "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
+ "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
+ "returned by getnetworkinfo) to enter the node's mempool.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The txid to be bumped"},
+ {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {
+ {"confTarget", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"totalFee", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis.\n"
" In rare cases, the actual fee paid might be slightly higher than the specified\n"
" totalFee if the tx change output has to be removed because it is too close to\n"
- " the dust threshold.\n"
- " \"replaceable\" (boolean, optional, default true) Whether the new transaction should still be\n"
+ " the dust threshold."},
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether the new transaction should still be\n"
" marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
" be left unchanged from the original. If false, any input sequence numbers in the\n"
" original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
- " are replaceable).\n"
- " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " are replaceable)."},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
- " }\n"
+ " \"CONSERVATIVE\""},
+ },
+ "options"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"txid\": \"value\", (string) The id of the new transaction\n"
@@ -3221,11 +3307,13 @@ UniValue generate(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "generate nblocks ( maxtries )\n"
- "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n"
- "\nArguments:\n"
- "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
- "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
+ RPCHelpMan{"generate",
+ "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."},
+ {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."},
+ }}
+ .ToString() +
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
@@ -3273,11 +3361,13 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "rescanblockchain (\"start_height\") (\"stop_height\")\n"
- "\nRescan the local blockchain for wallet related transactions.\n"
- "\nArguments:\n"
- "1. \"start_height\" (numeric, optional) block height where the rescan should start\n"
- "2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n"
+ RPCHelpMan{"rescanblockchain",
+ "\nRescan the local blockchain for wallet related transactions.\n",
+ {
+ {"start_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "block height where the rescan should start"},
+ {"stop_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "tip height", "the last block height that should be scanned"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n"
@@ -3480,11 +3570,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "getaddressinfo \"address\"\n"
- "\nReturn information about the given bitcoin address. Some information requires the address\n"
- "to be in the wallet.\n"
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to get the information of.\n"
+ RPCHelpMan{"getaddressinfo",
+ "\nReturn information about the given bitcoin address. Some information requires the address\n"
+ "to be in the wallet.\n",
+ {
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to get the information of."},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
@@ -3492,6 +3584,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"solvable\" : true|false, (boolean) If the address is solvable by the wallet\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
+ " \"solvable\" : true|false, (boolean) Whether we know how to spend coins sent to this address, ignoring the possible lack of private keys\n"
+ " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable)\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
" \"ischange\" : true|false, (boolean) If the address was used for change output\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
@@ -3545,6 +3639,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
+ bool solvable = IsSolvable(*pwallet, scriptPubKey);
+ ret.pushKV("solvable", solvable);
+ if (solvable) {
+ ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString());
+ }
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
ret.pushKV("solvable", IsSolvable(*pwallet, scriptPubKey));
UniValue detail = DescribeWalletAddress(pwallet, dest);
@@ -3600,10 +3699,12 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "getaddressesbylabel \"label\"\n"
- "\nReturns the list of addresses assigned the specified label.\n"
- "\nArguments:\n"
- "1. \"label\" (string, required) The label.\n"
+ RPCHelpMan{"getaddressesbylabel",
+ "\nReturns the list of addresses assigned the specified label.\n",
+ {
+ {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label."},
+ }}
+ .ToString() +
"\nResult:\n"
"{ (json object with addresses as keys)\n"
" \"address\": { (json object with information about address)\n"
@@ -3645,10 +3746,12 @@ static UniValue listlabels(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "listlabels ( \"purpose\" )\n"
- "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n"
- "\nArguments:\n"
- "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n"
+ RPCHelpMan{"listlabels",
+ "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
+ {
+ {"purpose", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
+ }}
+ .ToString() +
"\nResult:\n"
"[ (json array of string)\n"
" \"label\", (string) Label name\n"
@@ -3699,18 +3802,20 @@ UniValue sethdseed(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "sethdseed ( \"newkeypool\" \"seed\" )\n"
- "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
- "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
- "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
- + HelpRequiringPassphrase(pwallet) +
- "\nArguments:\n"
- "1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
+ RPCHelpMan{"sethdseed",
+ "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
+ "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
+ "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"newkeypool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
- " keypool will be used until it has been depleted.\n"
- "2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
- " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1\n"
+ " keypool will be used until it has been depleted."},
+ {"seed", RPCArg::Type::STR, /* opt */ true, /* default_val */ "random seed", "The WIF private key to use as the new HD seed.\n"
+ " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
+ }}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("sethdseed", "")
+ HelpExampleCli("sethdseed", "false")
@@ -3838,23 +3943,23 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
- "walletprocesspsbt \"psbt\" ( sign \"sighashtype\" bip32derivs )\n"
- "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
- "that we can sign for.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
-
- "\nArguments:\n"
- "1. \"psbt\" (string, required) The transaction base64 string\n"
- "2. sign (boolean, optional, default=true) Also sign the transaction when updating\n"
- "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ RPCHelpMan{"walletprocesspsbt",
+ "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
+ "that we can sign for." +
+ HelpRequiringPassphrase(pwallet) + "\n",
+ {
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction base64 string"},
+ {"sign", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Also sign the transaction when updating"},
+ {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
- " \"SINGLE|ANYONECANPAY\"\n"
- "4. bip32derivs (boolean, optional, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
-
+ " \"SINGLE|ANYONECANPAY\""},
+ {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction\n"
@@ -3903,55 +4008,68 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw std::runtime_error(
- "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n"
- "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
- "Implements the Creator and Updater roles.\n"
- "\nArguments:\n"
- "1. \"inputs\" (array, required) A json array of json objects\n"
- " [\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"sequence\":n (numeric, optional) The sequence number\n"
- " } \n"
- " ,...\n"
- " ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
- "4. options (object, optional)\n"
- " {\n"
- " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
- " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
- " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
- " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
- " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
- " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
- " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
+ RPCHelpMan{"walletcreatefundedpsbt",
+ "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "Implements the Creator and Updater roles.\n",
+ {
+ {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"sequence", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The sequence number"},
+ },
+ },
+ },
+ },
+ {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
+ "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.",
+ {
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
+ },
+ },
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {
+ {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ },
+ },
+ {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
+ {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {
+ {"changeAddress", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "pool address", "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "random", "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "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, /* opt */ true, /* default_val */ "false", "Also select inputs which are watch only"},
+ {"lockUnspents", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Lock selected unspent outputs"},
+ {"feeRate", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of integers.\n"
" The fee will be equally deducted from the amount of each specified output.\n"
- " The outputs are specified by their zero-based index, before any change output is added.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no outputs are specified here, the sender pays the fee.\n"
- " [vout_index,...]\n"
- " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees\n"
- " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
- " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " If no outputs are specified here, the sender pays the fee.",
+ {
+ {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
+ },
+ },
+ {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees"},
+ {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
- " }\n"
- "5. bip32derivs (boolean, optional, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
+ " \"CONSERVATIVE\""},
+ },
+ "options"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ }}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
@@ -4045,7 +4163,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
{ "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
- { "wallet", "listtransactions", &listtransactions, {"dummy","count","skip","include_watchonly"} },
+ { "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "listwalletdir", &listwalletdir, {} },
{ "wallet", "listwallets", &listwallets, {} },
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 46d9b5b238..5731a27e00 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -17,6 +17,7 @@
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
+#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
@@ -404,4 +405,47 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
}
+// Explicit calculation which is used to test the wallet constant
+// We get the same virtual size due to rounding(weight/4) for both use_max_sig values
+static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
+{
+ // Generate ephemeral valid pubkey
+ CKey key;
+ key.MakeNewKey(true);
+ CPubKey pubkey = key.GetPubKey();
+
+ // Generate pubkey hash
+ uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));
+
+ // Create inner-script to enter into keystore. Key hash can't be 0...
+ CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
+
+ // Create outer P2SH script for the output
+ uint160 script_id(Hash160(inner_script.begin(), inner_script.end()));
+ CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
+
+ // Add inner-script to key store and key to watchonly
+ CBasicKeyStore keystore;
+ keystore.AddCScript(inner_script);
+ keystore.AddKeyPubKey(key, pubkey);
+
+ // Fill in dummy signatures for fee calculation.
+ SignatureData sig_data;
+
+ if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
+ // We're hand-feeding it correct arguments; shouldn't happen
+ assert(false);
+ }
+
+ CTxIn tx_in;
+ UpdateInput(tx_in, sig_data);
+ return (size_t)GetVirtualTransactionInputSize(tx_in);
+}
+
+BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
+{
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 3c25a67fd1..829a1a2478 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1530,8 +1530,6 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionInputSize(txn.vin[0]);
@@ -2768,7 +2766,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
- coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
+ }
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
@@ -3885,11 +3890,9 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
}
// Make sure that the wallet path doesn't clash with an existing wallet path
- for (auto wallet : GetWallets()) {
- if (wallet->GetLocation().GetPath() == wallet_path) {
- error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
- return false;
- }
+ if (IsWalletLoaded(wallet_path)) {
+ error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
+ return false;
}
try {
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 5ba5514fc2..95a2c833f8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -85,6 +85,9 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+//! Pre-calculated constants for input size estimation in *virtual size*
+static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
+
class CBlockIndex;
class CCoinControl;
class COutput;
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index 0e0b745375..d3eab46e5f 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -5,6 +5,7 @@
#include <zmq/zmqrpc.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
@@ -16,8 +17,9 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "getzmqnotifications\n"
- "\nReturns information about the active ZeroMQ notifications.\n"
+ RPCHelpMan{"getzmqnotifications",
+ "\nReturns information about the active ZeroMQ notifications.\n", {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" { (json object)\n"