aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/addrman.cpp26
-rw-r--r--src/addrman.h5
-rw-r--r--src/arith_uint256.h3
-rw-r--r--src/bench/ccoins_caching.cpp9
-rw-r--r--src/bench/mempool_eviction.cpp2
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/chain.h9
-rw-r--r--src/httpserver.cpp32
-rw-r--r--src/init.cpp13
-rw-r--r--src/net.cpp13
-rw-r--r--src/net.h7
-rw-r--r--src/net_processing.cpp7
-rw-r--r--src/primitives/transaction.h8
-rw-r--r--src/qt/bitcoin.cpp23
-rw-r--r--src/qt/bitcoingui.cpp73
-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/guiutil.cpp2
-rw-r--r--src/qt/macnotificationhandler.mm23
-rwxr-xr-xsrc/qt/res/movies/makespinner.sh2
-rw-r--r--src/qt/rpcconsole.cpp10
-rw-r--r--src/qt/rpcconsole.h5
-rw-r--r--src/qt/splashscreen.cpp4
-rw-r--r--src/qt/splashscreen.h7
-rw-r--r--src/qt/test/wallettests.cpp8
-rw-r--r--src/random.cpp15
-rw-r--r--src/random.h31
-rw-r--r--src/rpc/blockchain.cpp158
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp103
-rw-r--r--src/rpc/misc.cpp83
-rw-r--r--src/rpc/net.cpp42
-rw-r--r--src/rpc/rawtransaction.cpp342
-rw-r--r--src/rpc/server.cpp27
-rw-r--r--src/rpc/util.cpp235
-rw-r--r--src/rpc/util.h66
-rw-r--r--src/script/descriptor.cpp444
-rw-r--r--src/script/descriptor.h12
-rw-r--r--src/script/sign.cpp2
-rw-r--r--src/streams.h4
-rw-r--r--src/test/addrman_tests.cpp72
-rw-r--r--src/test/checkqueue_tests.cpp3
-rw-r--r--src/test/coins_tests.cpp16
-rw-r--r--src/test/cuckoocache_tests.cpp47
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/denialofservice_tests.cpp28
-rw-r--r--src/test/descriptor_tests.cpp36
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/key_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp24
-rw-r--r--src/test/miner_tests.cpp36
-rw-r--r--src/test/multisig_tests.cpp14
-rw-r--r--src/test/net_tests.cpp24
-rw-r--r--src/test/policyestimator_tests.cpp2
-rw-r--r--src/test/prevector_tests.cpp4
-rw-r--r--src/test/random_tests.cpp51
-rw-r--r--src/test/rpc_tests.cpp3
-rw-r--r--src/test/script_p2sh_tests.cpp52
-rw-r--r--src/test/script_standard_tests.cpp94
-rw-r--r--src/test/script_tests.cpp64
-rw-r--r--src/test/serialize_tests.cpp2
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/sigopcount_tests.cpp14
-rw-r--r--src/test/test_bitcoin.cpp29
-rw-r--r--src/test/test_bitcoin.h39
-rw-r--r--src/test/transaction_tests.cpp82
-rw-r--r--src/test/txvalidationcache_tests.cpp34
-rw-r--r--src/test/validation_block_tests.cpp17
-rw-r--r--src/validation.cpp27
-rw-r--r--src/wallet/coinselection.cpp2
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/rpcdump.cpp136
-rw-r--r--src/wallet/rpcwallet.cpp726
-rw-r--r--src/wallet/test/wallet_tests.cpp67
-rw-r--r--src/wallet/wallet.cpp136
-rw-r--r--src/wallet/wallet.h8
79 files changed, 1922 insertions, 1877 deletions
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 44328c3056..06c342ba73 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -217,7 +217,7 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
return;
// find a bucket it is in now
- int nRnd = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
+ int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucket = -1;
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
@@ -291,7 +291,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
int nFactor = 1;
for (int n = 0; n < pinfo->nRefCount; n++)
nFactor *= 2;
- if (nFactor > 1 && (RandomInt(nFactor) != 0))
+ if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
return false;
} else {
pinfo = Create(addr, source, &nId);
@@ -356,12 +356,12 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
- (nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
+ (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
// use a tried node
double fChanceFactor = 1.0;
while (1) {
- int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
- int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
+ int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
+ int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
@@ -369,7 +369,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
CAddrInfo& info = mapInfo[nId];
- if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
@@ -377,8 +377,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
// use a new node
double fChanceFactor = 1.0;
while (1) {
- int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
- int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
+ int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
+ int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
@@ -386,7 +386,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
CAddrInfo& info = mapInfo[nId];
- if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
@@ -482,7 +482,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
if (vAddr.size() >= nNodes)
break;
- int nRndPos = RandomInt(vRandom.size() - n) + n;
+ int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
assert(mapInfo.count(vRandom[n]) == 1);
@@ -530,10 +530,6 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
info.nServices = nServices;
}
-int CAddrMan::RandomInt(int nMax){
- return GetRandInt(nMax);
-}
-
void CAddrMan::ResolveCollisions_()
{
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
@@ -593,7 +589,7 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
std::set<int>::iterator it = m_tried_collisions.begin();
// Selects a random element from m_tried_collisions
- std::advance(it, GetRandInt(m_tried_collisions.size()));
+ std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
int id_new = *it;
// If id_new not found in mapInfo remove it from m_tried_collisions
diff --git a/src/addrman.h b/src/addrman.h
index b97feb6f08..af5a1d3b23 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -266,9 +266,6 @@ protected:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
- //! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
- virtual int RandomInt(int nMax);
-
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -473,7 +470,7 @@ public:
{
LOCK(cs);
std::vector<int>().swap(vRandom);
- nKey = GetRandHash();
+ nKey = insecure_rand.rand256();
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
vvNew[bucket][entry] = -1;
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/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index b8d82c0a89..9cfd5d23ef 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -35,14 +35,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
dummyTransactions[0].vout[1].nValue = 50 * COIN;
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- AddCoins(coinsRet, dummyTransactions[0], 0);
+ AddCoins(coinsRet, CTransaction(dummyTransactions[0]), 0);
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21 * COIN;
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
dummyTransactions[1].vout[1].nValue = 22 * COIN;
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
- AddCoins(coinsRet, dummyTransactions[1], 0);
+ AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
}
@@ -76,10 +76,11 @@ static void CCoinsCaching(benchmark::State& state)
t1.vout[0].scriptPubKey << OP_1;
// Benchmark.
+ const CTransaction tx_1(t1);
while (state.KeepRunning()) {
- bool success = AreInputsStandard(t1, coins);
+ bool success = AreInputsStandard(tx_1, coins);
assert(success);
- CAmount value = coins.GetValueIn(t1);
+ CAmount value = coins.GetValueIn(tx_1);
assert(value == (50 + 21 + 22) * COIN);
}
}
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index 3908a7d231..49ea6e88b5 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -127,7 +127,7 @@ static void MempoolEviction(benchmark::State& state)
AddTx(tx6_r, 1100LL, pool);
AddTx(tx7_r, 9000LL, pool);
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
- pool.TrimToSize(GetVirtualTransactionSize(tx1));
+ pool.TrimToSize(GetVirtualTransactionSize(*tx1_r));
}
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 262458a684..7cdf61ad35 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -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/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/httpserver.cpp b/src/httpserver.cpp
index 00434169cd..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>
@@ -421,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()
@@ -429,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);
@@ -442,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);
}
@@ -465,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) {
@@ -583,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 31212a355b..8ecd79197f 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
@@ -499,7 +500,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("-rpcauth=<userpw>", "Username and HMAC-SHA-256 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. 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);
@@ -856,6 +857,7 @@ int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
+int64_t peer_connect_timeout;
} // namespace
@@ -1054,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;
@@ -1693,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/net.cpp b/src/net.cpp
index b85a8c2c1d..e595fb0b0b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -134,11 +134,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
vSeedsOut.reserve(vSeedsIn.size());
+ FastRandomContext rng;
for (const auto& seed_in : vSeedsIn) {
struct in6_addr ip;
memcpy(&ip, seed_in.addr, sizeof(ip));
CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE));
- addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
+ addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek;
vSeedsOut.push_back(addr);
}
return vSeedsOut;
@@ -189,16 +190,16 @@ void AdvertiseLocal(CNode *pnode)
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
+ FastRandomContext rng;
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
- GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
+ rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
{
addrLocal.SetIP(pnode->GetAddrLocal());
}
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
- FastRandomContext insecure_rand;
- pnode->PushAddress(addrLocal, insecure_rand);
+ pnode->PushAddress(addrLocal, rng);
}
}
}
@@ -1227,11 +1228,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 a059d32c89..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;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f4ab3aa153..0e222bdfa4 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.
@@ -779,10 +779,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
}
+ FastRandomContext rng;
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
- uint256 randomhash = GetRandHash();
+ uint256 randomhash = rng.rand256();
std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
@@ -1124,7 +1125,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/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/bitcoin.cpp b/src/qt/bitcoin.cpp
index de236a016f..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)
@@ -696,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/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ed705d6ba8..d7056ddd89 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>
@@ -57,6 +56,7 @@
#include <QToolBar>
#include <QUrlQuery>
#include <QVBoxLayout>
+#include <QWindow>
#include <boost/bind.hpp>
@@ -325,9 +325,9 @@ void BitcoinGUI::createActions()
// initially disable the debug window menu item
openRPCConsoleAction->setEnabled(false);
- usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this);
+ usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses"), this);
usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels"));
- usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this);
+ usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses"), this);
usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this);
@@ -386,9 +386,6 @@ void BitcoinGUI::createMenuBar()
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addSeparator();
- file->addAction(usedSendingAddressesAction);
- file->addAction(usedReceivingAddressesAction);
- file->addSeparator();
}
file->addAction(quitAction);
@@ -401,11 +398,64 @@ void BitcoinGUI::createMenuBar()
}
settings->addAction(optionsAction);
- QMenu *help = appMenuBar->addMenu(tr("&Help"));
- if(walletFrame)
- {
- help->addAction(openRPCConsoleAction);
+ QMenu* window_menu = appMenuBar->addMenu(tr("&Window"));
+
+ QAction* minimize_action = window_menu->addAction(tr("Minimize"));
+ minimize_action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
+ connect(minimize_action, &QAction::triggered, [] {
+ qApp->focusWindow()->showMinimized();
+ });
+ connect(qApp, &QApplication::focusWindowChanged, [minimize_action] (QWindow* window) {
+ minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized);
+ });
+
+#ifdef Q_OS_MAC
+ QAction* zoom_action = window_menu->addAction(tr("Zoom"));
+ connect(zoom_action, &QAction::triggered, [] {
+ QWindow* window = qApp->focusWindow();
+ if (window->windowState() != Qt::WindowMaximized) {
+ window->showMaximized();
+ } else {
+ window->showNormal();
+ }
+ });
+
+ connect(qApp, &QApplication::focusWindowChanged, [zoom_action] (QWindow* window) {
+ zoom_action->setEnabled(window != nullptr);
+ });
+#else
+ QAction* restore_action = window_menu->addAction(tr("Restore"));
+ connect(restore_action, &QAction::triggered, [] {
+ qApp->focusWindow()->showNormal();
+ });
+
+ connect(qApp, &QApplication::focusWindowChanged, [restore_action] (QWindow* window) {
+ restore_action->setEnabled(window != nullptr);
+ });
+#endif
+
+ if (walletFrame) {
+ window_menu->addSeparator();
+ QAction* main_window_action = window_menu->addAction(tr("Main Window"));
+ connect(main_window_action, &QAction::triggered, [this] {
+ GUIUtil::bringToFront(this);
+ });
+
+ window_menu->addSeparator();
+ window_menu->addAction(usedSendingAddressesAction);
+ window_menu->addAction(usedReceivingAddressesAction);
+ }
+
+ window_menu->addSeparator();
+ for (RPCConsole::TabTypes tab_type : rpcConsole->tabs()) {
+ QAction* tab_action = window_menu->addAction(rpcConsole->tabTitle(tab_type));
+ connect(tab_action, &QAction::triggered, [this, tab_type] {
+ rpcConsole->setTabFocus(tab_type);
+ showDebugWindow();
+ });
}
+
+ QMenu *help = appMenuBar->addMenu(tr("&Help"));
help->addAction(showHelpMessageAction);
help->addSeparator();
help->addAction(aboutAction);
@@ -1226,9 +1276,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/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/guiutil.cpp b/src/qt/guiutil.cpp
index 0e9aca21b1..2fc166b0c5 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -339,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/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm
index 0e04d50baa..a07079eece 100644
--- a/src/qt/macnotificationhandler.mm
+++ b/src/qt/macnotificationhandler.mm
@@ -24,25 +24,10 @@ void MacNotificationHandler::showNotification(const QString &title, const QStrin
{
// check if users OS has support for NSUserNotification
if(this->hasUserNotificationCenterSupport()) {
- // okay, seems like 10.8+
- QByteArray utf8 = title.toUtf8();
- char* cString = (char *)utf8.constData();
- NSString *titleMac = [[NSString alloc] initWithUTF8String:cString];
-
- utf8 = text.toUtf8();
- cString = (char *)utf8.constData();
- NSString *textMac = [[NSString alloc] initWithUTF8String:cString];
-
- // do everything weak linked (because we will keep <10.8 compatibility)
- id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init];
- [userNotification performSelector:@selector(setTitle:) withObject:titleMac];
- [userNotification performSelector:@selector(setInformativeText:) withObject:textMac];
-
- id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)];
- [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification];
-
- [titleMac release];
- [textMac release];
+ NSUserNotification* userNotification = [[NSUserNotification alloc] init];
+ userNotification.title = title.toNSString();
+ userNotification.informativeText = text.toNSString();
+ [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: userNotification];
[userNotification release];
}
}
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/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 606f1d2910..774a0d78e7 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1275,7 +1275,17 @@ void RPCConsole::showOrHideBanTableIfRequired()
ui->banHeading->setVisible(visible);
}
+RPCConsole::TabTypes RPCConsole::tabFocus() const
+{
+ return (TabTypes) ui->tabWidget->currentIndex();
+}
+
void RPCConsole::setTabFocus(enum TabTypes tabType)
{
ui->tabWidget->setCurrentIndex(tabType);
}
+
+QString RPCConsole::tabTitle(TabTypes tab_type) const
+{
+ return ui->tabWidget->tabText(tab_type);
+}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index db77043951..20dbf5ec95 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -65,6 +65,11 @@ public:
TAB_PEERS = 3
};
+ std::vector<TabTypes> tabs() const { return {TAB_INFO, TAB_CONSOLE, TAB_GRAPH, TAB_PEERS}; }
+
+ TabTypes tabFocus() const;
+ QString tabTitle(TabTypes tab_type) const;
+
protected:
virtual bool eventFilter(QObject* obj, QEvent *event);
void keyPressEvent(QKeyEvent *);
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index df38285d08..7b952f9fd7 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -145,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/wallettests.cpp b/src/qt/test/wallettests.cpp
index f02fd8aea7..610d83acb6 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -146,7 +146,13 @@ void TestGUI()
auto locked_chain = wallet->chain().lock();
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
- wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
+ const CBlockIndex* const null_block = nullptr;
+ const CBlockIndex *stop_block, *failed_block;
+ QCOMPARE(
+ wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */),
+ CWallet::ScanResult::SUCCESS);
+ QCOMPARE(stop_block, chainActive.Tip());
+ QCOMPARE(failed_block, null_block);
}
wallet->SetBroadcastTransactions(true);
diff --git a/src/random.cpp b/src/random.cpp
index a34c70e1d5..f8ffda136d 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -398,6 +398,7 @@ uint256 FastRandomContext::rand256()
std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
{
+ if (requires_seed) RandomSeed();
std::vector<unsigned char> ret(len);
if (len > 0) {
rng.Output(&ret[0], len);
@@ -463,6 +464,20 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete
rng.SetKey(seed.begin(), 32);
}
+FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept
+{
+ requires_seed = from.requires_seed;
+ rng = from.rng;
+ std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf));
+ bytebuf_size = from.bytebuf_size;
+ bitbuf = from.bitbuf;
+ bitbuf_size = from.bitbuf_size;
+ from.requires_seed = true;
+ from.bytebuf_size = 0;
+ from.bitbuf_size = 0;
+ return *this;
+}
+
void RandomInit()
{
RDRandInit();
diff --git a/src/random.h b/src/random.h
index 3d5421eb3e..00e90abbc5 100644
--- a/src/random.h
+++ b/src/random.h
@@ -76,6 +76,14 @@ public:
/** Initialize with explicit seed (only for testing) */
explicit FastRandomContext(const uint256& seed);
+ // Do not permit copying a FastRandomContext (move it, or create a new one to get reseeded).
+ FastRandomContext(const FastRandomContext&) = delete;
+ FastRandomContext(FastRandomContext&&) = delete;
+ FastRandomContext& operator=(const FastRandomContext&) = delete;
+
+ /** Move a FastRandomContext. If the original one is used again, it will be reseeded. */
+ FastRandomContext& operator=(FastRandomContext&& from) noexcept;
+
/** Generate a random 64-bit integer. */
uint64_t rand64()
{
@@ -130,6 +138,29 @@ public:
inline uint64_t operator()() { return rand64(); }
};
+/** More efficient than using std::shuffle on a FastRandomContext.
+ *
+ * This is more efficient as std::shuffle will consume entropy in groups of
+ * 64 bits at the time and throw away most.
+ *
+ * This also works around a bug in libstdc++ std::shuffle that may cause
+ * type::operator=(type&&) to be invoked on itself, which the library's
+ * debug mode detects and panics on. This is a known issue, see
+ * https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
+ */
+template<typename I, typename R>
+void Shuffle(I first, I last, R&& rng)
+{
+ while (first != last) {
+ size_t j = rng.randrange(last - first);
+ if (j) {
+ using std::swap;
+ swap(*first, *(first + j));
+ }
+ ++first;
+ }
+}
+
/* Number of random bytes returned by GetOSRand.
* When changing this constant make sure to change all call sites, and make
* sure that the underlying OS APIs for all platforms support the number.
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 403e3e397c..ec87f42c93 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -212,11 +212,9 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
"\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, true},
+ {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
}}
.ToString() +
- "\nArguments:\n"
- "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -254,13 +252,10 @@ static UniValue waitforblock(const JSONRPCRequest& request)
"\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, false},
- {"timeout", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -302,13 +297,10 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
"of the current tip.\n"
"\nReturns the current block on timeout or exit.\n",
{
- {"height", RPCArg::Type::NUM, false},
- {"timeout", RPCArg::Type::NUM, true},
+ {"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() +
- "\nArguments:\n"
- "1. height (int, required) Block height to wait for.\n"
- "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
@@ -496,14 +488,12 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
RPCHelpMan{"getrawmempool",
- "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\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",
{
- {"verbose", RPCArg::Type::BOOL, true},
+ {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
}}
.ToString() +
- "\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"
"\nResult: (for verbose = false):\n"
"[ (json array of string)\n"
" \"transactionid\" (string) The transaction id\n"
@@ -534,13 +524,10 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
RPCHelpMan{"getmempoolancestors",
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"verbose", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
@@ -603,13 +590,10 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
RPCHelpMan{"getmempooldescendants",
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"verbose", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
@@ -672,11 +656,9 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
RPCHelpMan{"getmempoolentry",
"\nReturns mempool data for given transaction\n",
{
- {"txid", RPCArg::Type::STR_HEX, false},
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"\nResult:\n"
"{ (json object)\n"
+ EntryDescriptionString()
@@ -709,11 +691,9 @@ static UniValue getblockhash(const JSONRPCRequest& request)
RPCHelpMan{"getblockhash",
"\nReturns hash of block in best-block-chain at height provided.\n",
{
- {"height", RPCArg::Type::NUM, false},
+ {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The height index"},
}}
.ToString() +
- "\nArguments:\n"
- "1. height (numeric, required) The height index\n"
"\nResult:\n"
"\"hash\" (string) The block hash\n"
"\nExamples:\n"
@@ -739,13 +719,10 @@ static UniValue getblockheader(const JSONRPCRequest& request)
"\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, false},
- {"verbose", RPCArg::Type::BOOL, true},
+ {"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() +
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) The block hash\n"
- "2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
@@ -823,13 +800,10 @@ static UniValue getblock(const JSONRPCRequest& request)
"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, false},
- {"verbosity", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult (for verbosity = 0):\n"
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
"\nResult (for verbosity = 1):\n"
@@ -977,12 +951,10 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"pruneblockchain", "",
{
- {"height", RPCArg::Type::NUM, false},
+ {"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() +
- "\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"
"\nResult:\n"
"n (numeric) Height of the last block pruned.\n"
"\nExamples:\n"
@@ -1075,16 +1047,11 @@ UniValue gettxout(const JSONRPCRequest& request)
RPCHelpMan{"gettxout",
"\nReturns details about an unspent transaction output.\n",
{
- {"txid", RPCArg::Type::STR, false},
- {"n", RPCArg::Type::NUM, false},
- {"include_mempool", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{\n"
" \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n"
@@ -1161,13 +1128,10 @@ static UniValue verifychain(const JSONRPCRequest& request)
RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
- {"checklevel", RPCArg::Type::NUM, true},
- {"nblocks", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"true|false (boolean) Verified or not\n"
"\nExamples:\n"
@@ -1458,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)) {
@@ -1526,11 +1490,9 @@ static UniValue preciousblock(const JSONRPCRequest& request)
"\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, false},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as precious"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to mark as precious\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("preciousblock", "\"blockhash\"")
@@ -1565,11 +1527,9 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
RPCHelpMan{"invalidateblock",
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, false},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as invalid"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to mark as invalid\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("invalidateblock", "\"blockhash\"")
@@ -1608,11 +1568,9 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
"\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, false},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to reconsider"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"blockhash\" (string, required) the hash of the block to reconsider\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("reconsiderblock", "\"blockhash\"")
@@ -1648,13 +1606,10 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
RPCHelpMan{"getchaintxstats",
"\nCompute statistics about the total number and rate of transactions in the chain.\n",
{
- {"nblocks", RPCArg::Type::NUM, true},
- {"blockhash", RPCArg::Type::STR_HEX, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{\n"
" \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n"
@@ -1785,23 +1740,15 @@ static UniValue getblockstats(const JSONRPCRequest& request)
"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, false},
- {"stats", RPCArg::Type::ARR,
+ {"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, true},
- {"time", RPCArg::Type::STR, true},
+ {"height", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
+ {"time", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
},
- true, "stats"},
+ "stats"},
}}
.ToString() +
- "\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"
"\nResult:\n"
"{ (json object)\n"
" \"avgfee\": xxxxx, (numeric) Average fee in the block\n"
@@ -2153,33 +2100,24 @@ UniValue scantxoutset(const JSONRPCRequest& request)
"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, false},
- {"scanobjects", RPCArg::Type::ARR,
+ {"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"},
+ {"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::OBJ,
+ {"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, false},
- {"range", RPCArg::Type::NUM, true},
+ {"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"},
},
- false, "scanobjects"},
+ },
},
- false},
+ "[scanobjects,...]"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"action\" (string, required) 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"
"\nResult:\n"
"{\n"
" \"unspents\": [\n"
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 c0287ec17f..29684d0e6c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -92,13 +92,10 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
"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, true},
- {"height", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"x (numeric) Hashes per second estimated\n"
"\nExamples:\n"
@@ -165,15 +162,11 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
- {"nblocks", RPCArg::Type::NUM, false},
- {"address", RPCArg::Type::STR, false},
- {"maxtries", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
@@ -247,19 +240,15 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
- {"txid", RPCArg::Type::STR, false},
- {"dummy", RPCArg::Type::NUM, false},
- {"fee_delta", RPCArg::Type::NUM, false},
- }}
- .ToString() +
- "\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"
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id."},
+ {"dummy", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "null", "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"
@@ -322,38 +311,23 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" 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,
+ {"template_request", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "A json object in the following spec",
{
- {"mode", RPCArg::Type::STR, true},
- {"capabilities", RPCArg::Type::ARR,
+ {"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, true},
+ {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
},
- true},
- {"rules", RPCArg::Type::ARR,
+ },
+ {"rules", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
{
- {"support", RPCArg::Type::STR, true},
+ {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"},
+ },
},
- true},
},
- true, "\"template_request\""},
+ "\"template_request\""},
}}
.ToString() +
- "\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"
-
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The preferred block version\n"
@@ -742,13 +716,10 @@ static UniValue submitblock(const JSONRPCRequest& request)
"\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, false},
- {"dummy", RPCArg::Type::STR, true},
+ {"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() +
- "\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"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("submitblock", "\"mydata\"")
@@ -810,11 +781,9 @@ static UniValue submitheader(const JSONRPCRequest& request)
"\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, false},
+ {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block header data"},
}}
.ToString() +
- "\nArguments\n"
- "1. \"hexdata\" (string, required) the hex-encoded block header data\n"
"\nResult:\n"
"None"
"\nExamples:\n" +
@@ -852,13 +821,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
"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, false},
- {"estimate_mode", RPCArg::Type::STR, true},
- }}
- .ToString() +
- "\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"
+ {"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"
@@ -866,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"
@@ -921,15 +887,12 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
"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, false},
- {"threshold", RPCArg::Type::NUM, true},
+ {"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."},
}}
.ToString() +
- "\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"
- " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
- " lower buckets. Default: 0.95\n"
"\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 5543035885..18c867bff3 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -39,11 +39,9 @@ static UniValue validateaddress(const JSONRPCRequest& request)
"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, false},
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
@@ -82,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"
@@ -149,15 +148,11 @@ static UniValue verifymessage(const JSONRPCRequest& request)
RPCHelpMan{"verifymessage",
"\nVerify a signed message\n",
{
- {"address", RPCArg::Type::STR, false},
- {"signature", RPCArg::Type::STR, false},
- {"message", RPCArg::Type::STR, false},
+ {"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() +
- "\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"
"\nResult:\n"
"true|false (boolean) If the signature is verified or not.\n"
"\nExamples:\n"
@@ -211,13 +206,10 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
RPCHelpMan{"signmessagewithprivkey",
"\nSign a message with the private key of an address\n",
{
- {"privkey", RPCArg::Type::STR, false},
- {"message", RPCArg::Type::STR, false},
+ {"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() +
- "\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"
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
"\nExamples:\n"
@@ -255,12 +247,10 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
- {"timestamp", RPCArg::Type::NUM, false},
+ {"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() +
- "\nArguments:\n"
- "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
- " Pass 0 to go back to using the system time."
+ .ToString()
);
if (!Params().MineBlocksOnDemand())
@@ -321,13 +311,11 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
RPCHelpMan{"getmemoryinfo",
"Returns an object containing information about memory usage.\n",
{
- {"mode", RPCArg::Type::STR, true},
+ {"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+)."},
}}
.ToString() +
- "Arguments:\n"
- "1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\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"
"\nResult (mode \"stats\"):\n"
"{\n"
" \"locked\": { (json object) Information about locked memory manager\n"
@@ -396,21 +384,16 @@ UniValue logging(const JSONRPCRequest& request)
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
,
{
- {"include", RPCArg::Type::STR, true},
- {"exclude", RPCArg::Type::STR, true},
+ {"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() +
- "\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"
"\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"
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 795b9b089b..4d6b260cc7 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -211,13 +211,10 @@ static UniValue addnode(const JSONRPCRequest& request)
"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, false},
- {"command", RPCArg::Type::STR, false},
+ {"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() +
- "\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"
"\nExamples:\n"
+ HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"")
+ HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
@@ -258,13 +255,10 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
"\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, true},
- {"nodeid", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleCli("disconnectnode", "\"\" 1")
@@ -305,11 +299,9 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
"\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, true},
+ {"node", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n"
"\nResult:\n"
"[\n"
" {\n"
@@ -524,17 +516,12 @@ static UniValue setban(const JSONRPCRequest& request)
RPCHelpMan{"setban",
"\nAttempts to add or remove an IP/Subnet from the banned list.\n",
{
- {"subnet", RPCArg::Type::STR, false},
- {"command", RPCArg::Type::STR, false},
- {"bantime", RPCArg::Type::NUM, true},
- {"absolute", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nExamples:\n"
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
@@ -644,11 +631,9 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
RPCHelpMan{"setnetworkactive",
"\nDisable/enable all p2p network activity.\n",
{
- {"state", RPCArg::Type::BOOL, false},
+ {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"},
}}
- .ToString() +
- "\nArguments:\n"
- "1. \"state\" (boolean, required) true to enable networking, false to disable\n"
+ .ToString()
);
}
@@ -668,12 +653,9 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
RPCHelpMan{"getnodeaddresses",
"\nReturn known addresses which can potentially be used to find new nodes in the network\n",
{
- {"count", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"[\n"
" {\n"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 41f3ac6b4a..370ae8e4d7 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -71,24 +71,19 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
"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",
- {
- {"txid", RPCArg::Type::STR_HEX, false},
- {"verbose", RPCArg::Type::BOOL, true},
- {"blockhash", RPCArg::Type::STR_HEX, true},
- }}
- .ToString() +
+ "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"
@@ -218,21 +213,14 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
"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,
+ {"txids", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of txids to filter",
{
- {"txid", RPCArg::Type::STR_HEX, false},
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ },
},
- false},
- {"blockhash", RPCArg::Type::STR_HEX, true},
+ {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "If specified, looks for txid in the block with this hash"},
}}
.ToString() +
- "\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"
"\nResult:\n"
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
);
@@ -316,11 +304,9 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
"\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, false},
+ {"proof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded proof generated by gettxoutproof"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n"
"\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
);
@@ -411,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);
@@ -427,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);
@@ -462,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"
@@ -502,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
);
}
@@ -526,15 +527,11 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
RPCHelpMan{"decoderawtransaction",
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, false},
- {"iswitness", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
-
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
@@ -605,11 +602,9 @@ static UniValue decodescript(const JSONRPCRequest& request)
RPCHelpMan{"decodescript",
"\nDecode a hex-encoded script.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded script"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"hexstring\" (string) the hex-encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
@@ -706,20 +701,13 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
"The combined transaction may be another partially signed transaction or a \n"
"fully signed transaction.",
{
- {"txs", RPCArg::Type::ARR,
+ {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex strings of partially signed transactions",
{
- {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ },
},
- false},
}}
.ToString() +
- "\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"
-
"\nResult:\n"
"\"hex\" (string) The hex-encoded raw transaction with signature(s)\n"
@@ -938,54 +926,35 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
"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, false},
- {"privkeys", RPCArg::Type::ARR,
+ {"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, false},
+ {"privatekey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "private key in base58-encoding"},
},
- false},
- {"prevtxs", RPCArg::Type::ARR,
+ },
+ {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs",
{
- {"", RPCArg::Type::OBJ,
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"vout", RPCArg::Type::NUM, false},
- {"scriptPubKey", RPCArg::Type::STR_HEX, false},
- {"redeemScript", RPCArg::Type::STR_HEX, false},
- {"amount", RPCArg::Type::AMOUNT, false},
+ {"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"},
+ },
},
- true},
},
- true},
- {"sighashtype", RPCArg::Type::STR, true},
- }}
- .ToString() +
- "\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"
+ },
+ {"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"
@@ -1044,13 +1013,10 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, false},
- {"allowhighfees", RPCArg::Type::BOOL, true},
+ {"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() +
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
- "2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
@@ -1140,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 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"
- "\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"
@@ -1167,7 +1138,6 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
- // clang-format on
);
}
@@ -1241,12 +1211,9 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCHelpMan{"decodepsbt",
"\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
{
- {"psbt", RPCArg::Type::STR, false},
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The PSBT base64 string"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"psbt\" (string, required) The PSBT base64 string\n"
-
"\nResult:\n"
"{\n"
" \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
@@ -1520,20 +1487,13 @@ UniValue combinepsbt(const JSONRPCRequest& request)
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n",
{
- {"txs", RPCArg::Type::ARR,
+ {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base64 strings of partially signed transactions",
{
- {"psbt", RPCArg::Type::STR_HEX, false},
+ {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"},
+ },
},
- false},
}}
.ToString() +
- "\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"
-
"\nResult:\n"
" \"psbt\" (string) The base64-encoded partially signed transaction\n"
"\nExamples:\n"
@@ -1583,15 +1543,11 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
"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, false},
- {"extract", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
-
"\nResult:\n"
"{\n"
" \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n"
@@ -1650,59 +1606,39 @@ UniValue createpsbt(const JSONRPCRequest& request)
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n",
{
- {"inputs", RPCArg::Type::ARR,
+ {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
{
- {"", RPCArg::Type::OBJ,
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"vout", RPCArg::Type::NUM, false},
- {"sequence", RPCArg::Type::NUM, true},
+ {"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"},
+ },
},
- false},
},
- false},
- {"outputs", RPCArg::Type::ARR,
+ },
+ {"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,
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"address", RPCArg::Type::AMOUNT, false},
+ {"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},
+ },
},
- true},
- {"", RPCArg::Type::OBJ,
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"data", RPCArg::Type::STR_HEX, false},
+ {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
},
- true},
+ },
+ },
},
- false},
- {"locktime", RPCArg::Type::NUM, true},
- {"replaceable", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult:\n"
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
"\nExamples:\n"
@@ -1745,19 +1681,15 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\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, false},
- {"permitsigdata", RPCArg::Type::BOOL, true},
- {"iswitness", RPCArg::Type::BOOL, true},
- }}
- .ToString() +
- "\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"
+ {"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 c565094a10..03e1c81132 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -24,7 +24,7 @@
#include <unordered_map>
static CCriticalSection cs_rpcWarmup;
-static bool fRPCRunning = false;
+static std::atomic<bool> g_rpc_running{false};
static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true;
static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started";
/* Timer-creating functions */
@@ -142,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;
@@ -207,11 +203,9 @@ UniValue help(const JSONRPCRequest& jsonRequest)
RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
- {"command", RPCArg::Type::STR, true},
+ {"command", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all commands", "The command to get help on"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"command\" (string, optional) The command to get help on\n"
"\nResult:\n"
"\"text\" (string) The help text\n"
);
@@ -227,6 +221,9 @@ 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(
RPCHelpMan{"stop",
@@ -235,6 +232,9 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
// 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";
}
@@ -256,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
@@ -306,7 +303,7 @@ bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
void StartRPC()
{
LogPrint(BCLog::RPC, "Starting RPC\n");
- fRPCRunning = true;
+ g_rpc_running = true;
g_rpcSignals.Started();
}
@@ -314,7 +311,7 @@ void InterruptRPC()
{
LogPrint(BCLog::RPC, "Interrupting RPC\n");
// Interrupt e.g. running longpolls
- fRPCRunning = false;
+ g_rpc_running = false;
}
void StopRPC()
@@ -327,7 +324,7 @@ void StopRPC()
bool IsRPCRunning()
{
- return fRPCRunning;
+ return g_rpc_running;
}
void SetRPCWarmupStatus(const std::string& newStatus)
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 0b6bbcb1dc..b91baee4ac 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -129,35 +129,240 @@ 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};
+ bool was_optional{false};
for (const auto& arg : m_args) {
ret += " ";
if (arg.m_optional) {
- if (!is_optional) ret += "( ";
- is_optional = true;
+ if (!was_optional) ret += "( ";
+ was_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);
+ if (was_optional) ret += ") ";
+ was_optional = false;
}
- ret += arg.ToString();
+ ret += arg.ToString(/* oneline */ true);
}
- if (is_optional) ret += " )";
+ if (was_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
+ sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + arg.m_name, 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::ToStringObj() const
+std::string RPCArg::ToDescriptionString(const bool implicitly_required) const
{
- std::string res = "\"" + m_name + "\":";
+ 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\"";
@@ -172,7 +377,7 @@ std::string RPCArg::ToStringObj() const
case Type::ARR:
res += "[";
for (const auto& i : m_inner) {
- res += i.ToString() + ",";
+ res += i.ToString(oneline) + ",";
}
return res + "...]";
case Type::OBJ:
@@ -185,9 +390,9 @@ std::string RPCArg::ToStringObj() const
assert(false);
}
-std::string RPCArg::ToString() const
+std::string RPCArg::ToString(const bool oneline) const
{
- if (!m_oneline_description.empty()) return m_oneline_description;
+ if (oneline && !m_oneline_description.empty()) return m_oneline_description;
switch (m_type) {
case Type::STR_HEX:
@@ -203,7 +408,7 @@ std::string RPCArg::ToString() const
case Type::OBJ_USER_KEYS: {
std::string res;
for (size_t i = 0; i < m_inner.size();) {
- res += m_inner[i].ToStringObj();
+ res += m_inner[i].ToStringObj(oneline);
if (++i < m_inner.size()) res += ",";
}
if (m_type == Type::OBJ) {
@@ -215,7 +420,7 @@ std::string RPCArg::ToString() const
case Type::ARR: {
std::string res;
for (const auto& i : m_inner) {
- res += i.ToString() + ",";
+ res += i.ToString(oneline) + ",";
}
return "[" + res + "...]";
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index b1ab64247c..9e97b3ae0b 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -43,33 +43,73 @@ struct RPCArg {
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
-
- RPCArg(const std::string& name, const Type& type, const bool optional, const std::string& oneline_description = "")
- : m_name{name}, m_type{type}, m_optional{optional}, m_oneline_description{oneline_description}
+ 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 std::vector<RPCArg>& inner, const bool optional, const std::string& oneline_description = "")
- : m_name{name}, m_type{type}, m_inner{inner}, m_optional{optional}, m_oneline_description{oneline_description}
+ 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);
}
- std::string ToString() const;
-
-private:
- std::string ToStringObj() const;
+ /**
+ * 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)
- : m_name{name}, m_description{description}, m_args{args}
- {
- }
+ RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args);
std::string ToString() const;
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index ca80d3451f..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,129 +204,119 @@ public:
}
};
-/** A parsed addr(A) descriptor. */
-class AddressDescriptor final : public Descriptor
-{
- CTxDestination m_destination;
-
-public:
- AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
-
- bool IsRange() const override { return false; }
- bool IsSolvable() 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
- {
- 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; }
- bool IsSolvable() 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
+/** Base class for all Descriptor implementations. */
+class DescriptorImpl : public Descriptor
{
- const std::function<CScript(const CPubKey&)> m_script_fn;
- const std::string m_fn_name;
- std::unique_ptr<PubkeyProvider> m_provider;
+ //! 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:
- 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)) {}
+ 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 m_provider->IsRange(); }
- bool IsSolvable() const override { return true; }
- 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
+ bool IsSolvable() 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;
}
- bool IsSolvable() const override { return true; }
-
- 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) {
@@ -332,89 +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(); }
- bool IsSolvable() const override { return m_descriptor->IsSolvable(); }
- 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(); }
- bool IsSolvable() const override { return true; }
- 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));
- }
- 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));
+ 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
}
- 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") {}
};
////////////////////////////////////////////////////////////////////////////
@@ -562,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);
@@ -606,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()));
@@ -642,7 +686,7 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo
return key_provider;
}
-std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& 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);
@@ -650,7 +694,7 @@ std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContex
if (txntype == TX_PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
- return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk");
+ return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TX_PUBKEYHASH) {
@@ -658,7 +702,7 @@ std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContex
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
- return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh");
+ return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
@@ -666,7 +710,7 @@ std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContex
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
- return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2WPKHGetScript, "wpkh");
+ return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TX_MULTISIG) {
@@ -683,7 +727,7 @@ std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContex
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
- if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2SH, "sh");
+ if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
}
}
if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
@@ -692,7 +736,7 @@ std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContex
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
- if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2WSH, "wsh");
+ if (sub) return MakeUnique<WSHDescriptor>(std::move(sub));
}
}
@@ -712,7 +756,7 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv
{
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;
}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 0111972f85..44f0efca03 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -48,8 +48,18 @@ 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. */
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index e5651710f1..635e4fa3d2 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -705,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/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 55fe19cebe..234da5ae4d 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -32,12 +32,6 @@ public:
insecure_rand = FastRandomContext(true);
}
- int RandomInt(int nMax) override
- {
- state = (CHashWriter(SER_GETHASH, 0) << state).GetCheapHash();
- return (unsigned int)(state % nMax);
- }
-
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
{
LOCK(cs);
@@ -154,11 +148,11 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
// Test 7; Addr with same IP but diff port does not replace existing addr.
CService addr1 = ResolveService("250.1.1.1", 8333);
- addrman.Add(CAddress(addr1, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334);
- addrman.Add(CAddress(addr1_port, NODE_NONE), source);
+ BOOST_CHECK(!addrman.Add(CAddress(addr1_port, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
CAddrInfo addr_ret2 = addrman.Select();
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
@@ -181,7 +175,7 @@ BOOST_AUTO_TEST_CASE(addrman_select)
// Test: Select from new with 1 addr in new.
CService addr1 = ResolveService("250.1.1.1", 8333);
- addrman.Add(CAddress(addr1, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
bool newOnly = true;
@@ -205,20 +199,20 @@ BOOST_AUTO_TEST_CASE(addrman_select)
CService addr3 = ResolveService("250.3.2.2", 9999);
CService addr4 = ResolveService("250.3.3.3", 9999);
- addrman.Add(CAddress(addr2, NODE_NONE), ResolveService("250.3.1.1", 8333));
- addrman.Add(CAddress(addr3, NODE_NONE), ResolveService("250.3.1.1", 8333));
- addrman.Add(CAddress(addr4, NODE_NONE), ResolveService("250.4.1.1", 8333));
+ BOOST_CHECK(addrman.Add(CAddress(addr2, NODE_NONE), ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman.Add(CAddress(addr3, NODE_NONE), ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman.Add(CAddress(addr4, NODE_NONE), ResolveService("250.4.1.1", 8333)));
// Add three addresses to tried table.
CService addr5 = ResolveService("250.4.4.4", 8333);
CService addr6 = ResolveService("250.4.5.5", 7777);
CService addr7 = ResolveService("250.4.6.6", 8333);
- addrman.Add(CAddress(addr5, NODE_NONE), ResolveService("250.3.1.1", 8333));
+ BOOST_CHECK(addrman.Add(CAddress(addr5, NODE_NONE), ResolveService("250.3.1.1", 8333)));
addrman.Good(CAddress(addr5, NODE_NONE));
- addrman.Add(CAddress(addr6, NODE_NONE), ResolveService("250.3.1.1", 8333));
+ BOOST_CHECK(addrman.Add(CAddress(addr6, NODE_NONE), ResolveService("250.3.1.1", 8333)));
addrman.Good(CAddress(addr6, NODE_NONE));
- addrman.Add(CAddress(addr7, NODE_NONE), ResolveService("250.1.1.3", 8333));
+ BOOST_CHECK(addrman.Add(CAddress(addr7, NODE_NONE), ResolveService("250.1.1.3", 8333)));
addrman.Good(CAddress(addr7, NODE_NONE));
// Test: 6 addrs + 1 addr from last test = 7.
@@ -242,7 +236,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
for (unsigned int i = 1; i < 18; i++) {
CService addr = ResolveService("250.1.1." + std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
//Test: No collision in new table yet.
BOOST_CHECK_EQUAL(addrman.size(), i);
@@ -250,11 +244,11 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
//Test: new table collision!
CService addr1 = ResolveService("250.1.1.18");
- addrman.Add(CAddress(addr1, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 17U);
CService addr2 = ResolveService("250.1.1.19");
- addrman.Add(CAddress(addr2, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr2, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 18U);
}
@@ -268,7 +262,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
for (unsigned int i = 1; i < 80; i++) {
CService addr = ResolveService("250.1.1." + std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(CAddress(addr, NODE_NONE));
//Test: No collision in tried table yet.
@@ -277,11 +271,11 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
//Test: tried table collision!
CService addr1 = ResolveService("250.1.1.80");
- addrman.Add(CAddress(addr1, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 79U);
CService addr2 = ResolveService("250.1.1.81");
- addrman.Add(CAddress(addr2, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr2, NODE_NONE), source));
BOOST_CHECK_EQUAL(addrman.size(), 80U);
}
@@ -298,9 +292,9 @@ BOOST_AUTO_TEST_CASE(addrman_find)
CNetAddr source1 = ResolveIP("250.1.2.1");
CNetAddr source2 = ResolveIP("250.1.2.2");
- addrman.Add(addr1, source1);
- addrman.Add(addr2, source2);
- addrman.Add(addr3, source1);
+ BOOST_CHECK(addrman.Add(addr1, source1));
+ BOOST_CHECK(!addrman.Add(addr2, source2));
+ BOOST_CHECK(addrman.Add(addr3, source1));
// Test: ensure Find returns an IP matching what we searched on.
CAddrInfo* info1 = addrman.Find(addr1);
@@ -382,11 +376,11 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
CNetAddr source2 = ResolveIP("250.2.3.3");
// Test: Ensure GetAddr works with new addresses.
- addrman.Add(addr1, source1);
- addrman.Add(addr2, source2);
- addrman.Add(addr3, source1);
- addrman.Add(addr4, source2);
- addrman.Add(addr5, source1);
+ BOOST_CHECK(addrman.Add(addr1, source1));
+ BOOST_CHECK(addrman.Add(addr2, source2));
+ BOOST_CHECK(addrman.Add(addr3, source1));
+ BOOST_CHECK(addrman.Add(addr4, source2));
+ BOOST_CHECK(addrman.Add(addr5, source1));
// GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down.
BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1U);
@@ -555,7 +549,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) {
CService addr = ResolveService("250.1.1."+std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
// No collisions yet.
@@ -585,7 +579,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) {
CService addr = ResolveService("250.1.1."+std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
// No collision yet.
@@ -595,7 +589,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
// Collision between 23 and 19.
CService addr23 = ResolveService("250.1.1.23");
- addrman.Add(CAddress(addr23, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr23, NODE_NONE), source));
addrman.Good(addr23);
BOOST_CHECK(addrman.size() == 23);
@@ -608,7 +602,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
// Lets create two collisions.
for (unsigned int i = 24; i < 33; i++) {
CService addr = ResolveService("250.1.1."+std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
BOOST_CHECK(addrman.size() == i);
@@ -617,14 +611,14 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
// Cause a collision.
CService addr33 = ResolveService("250.1.1.33");
- addrman.Add(CAddress(addr33, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr33, NODE_NONE), source));
addrman.Good(addr33);
BOOST_CHECK(addrman.size() == 33);
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.27:0");
// Cause a second collision.
- addrman.Add(CAddress(addr23, NODE_NONE), source);
+ BOOST_CHECK(!addrman.Add(CAddress(addr23, NODE_NONE), source));
addrman.Good(addr23);
BOOST_CHECK(addrman.size() == 33);
@@ -649,7 +643,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) {
CService addr = ResolveService("250.1.1."+std::to_string(i));
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
// No collision yet.
@@ -659,7 +653,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
// Collision between 23 and 19.
CService addr = ResolveService("250.1.1.23");
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
BOOST_CHECK(addrman.size() == 23);
@@ -674,14 +668,14 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
// If 23 was swapped for 19, then this should cause no collisions.
- addrman.Add(CAddress(addr, NODE_NONE), source);
+ BOOST_CHECK(!addrman.Add(CAddress(addr, NODE_NONE), source));
addrman.Good(addr);
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
// If we insert 19 is should collide with 23.
CService addr19 = ResolveService("250.1.1.19");
- addrman.Add(CAddress(addr19, NODE_NONE), source);
+ BOOST_CHECK(!addrman.Add(CAddress(addr19, NODE_NONE), source));
addrman.Good(addr19);
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.23:0");
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index a757e06a9d..3469c6dfba 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -355,7 +355,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
// would get called twice).
vChecks[0].should_freeze = true;
control.Add(vChecks);
- control.Wait(); // Hangs here
+ bool waitResult = control.Wait(); // Hangs here
+ assert(waitResult);
});
{
std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index d3cbaedf00..aa2e88477d 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
} else {
removed_an_entry = true;
coin.Clear();
- stack.back()->SpendCoin(COutPoint(txid, 0));
+ BOOST_CHECK(stack.back()->SpendCoin(COutPoint(txid, 0)));
}
}
@@ -211,14 +211,14 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// Every 100 iterations, flush an intermediate cache
if (stack.size() > 1 && InsecureRandBool() == 0) {
unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
- stack[flushIndex]->Flush();
+ BOOST_CHECK(stack[flushIndex]->Flush());
}
}
if (InsecureRandRange(100) == 0) {
// Every 100 iterations, change the cache stack.
if (stack.size() > 0 && InsecureRandBool() == 0) {
//Remove the top cache
- stack.back()->Flush();
+ BOOST_CHECK(stack.back()->Flush());
delete stack.back();
stack.pop_back();
}
@@ -377,7 +377,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Call UpdateCoins on the top cache
CTxUndo undo;
- UpdateCoins(tx, *(stack.back()), undo, height);
+ UpdateCoins(CTransaction(tx), *(stack.back()), undo, height);
// Update the utxo set for future spends
utxoset.insert(outpoint);
@@ -403,7 +403,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Disconnect the tx from the current UTXO
// See code in DisconnectBlock
// remove outputs
- stack.back()->SpendCoin(utxod->first);
+ BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
// restore inputs
if (!tx.IsCoinBase()) {
const COutPoint &out = tx.vin[0].prevout;
@@ -444,13 +444,13 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Every 100 iterations, flush an intermediate cache
if (stack.size() > 1 && InsecureRandBool() == 0) {
unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
- stack[flushIndex]->Flush();
+ BOOST_CHECK(stack[flushIndex]->Flush());
}
}
if (InsecureRandRange(100) == 0) {
// Every 100 iterations, change the cache stack.
if (stack.size() > 0 && InsecureRandBool() == 0) {
- stack.back()->Flush();
+ BOOST_CHECK(stack.back()->Flush());
delete stack.back();
stack.pop_back();
}
@@ -589,7 +589,7 @@ void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{
CCoinsMap map;
InsertCoinsMapEntry(map, value, flags);
- view.BatchWrite(map, {});
+ BOOST_CHECK(view.BatchWrite(map, {}));
}
class SingleEntryCacheTest
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index dbceb9d2e0..d8286520ec 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -21,40 +21,23 @@
* using BOOST_CHECK_CLOSE to fail.
*
*/
-FastRandomContext local_rand_ctx(true);
-
BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
-
-/** insecure_GetRandHash fills in a uint256 from local_rand_ctx
- */
-static void insecure_GetRandHash(uint256& t)
-{
- uint32_t* ptr = (uint32_t*)t.begin();
- for (uint8_t j = 0; j < 8; ++j)
- *(ptr++) = local_rand_ctx.rand32();
-}
-
-
-
/* Test that no values not inserted into the cache are read out of it.
*
* There are no repeats in the first 200000 insecure_GetRandHash calls
*/
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
- local_rand_ctx = FastRandomContext(true);
+ SeedInsecureRand(true);
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
- uint256 v;
for (int x = 0; x < 100000; ++x) {
- insecure_GetRandHash(v);
- cc.insert(v);
+ cc.insert(InsecureRand256());
}
for (int x = 0; x < 100000; ++x) {
- insecure_GetRandHash(v);
- BOOST_CHECK(!cc.contains(v, false));
+ BOOST_CHECK(!cc.contains(InsecureRand256(), false));
}
};
@@ -64,7 +47,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
template <typename Cache>
static double test_cache(size_t megabytes, double load)
{
- local_rand_ctx = FastRandomContext(true);
+ SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -74,7 +57,7 @@ static double test_cache(size_t megabytes, double load)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
for (uint8_t j = 0; j < 8; ++j)
- *(ptr++) = local_rand_ctx.rand32();
+ *(ptr++) = InsecureRand32();
}
/** We make a copy of the hashes because future optimizations of the
* cuckoocache may overwrite the inserted element, so the test is
@@ -135,7 +118,7 @@ template <typename Cache>
static void test_cache_erase(size_t megabytes)
{
double load = 1;
- local_rand_ctx = FastRandomContext(true);
+ SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -145,7 +128,7 @@ static void test_cache_erase(size_t megabytes)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
for (uint8_t j = 0; j < 8; ++j)
- *(ptr++) = local_rand_ctx.rand32();
+ *(ptr++) = InsecureRand32();
}
/** We make a copy of the hashes because future optimizations of the
* cuckoocache may overwrite the inserted element, so the test is
@@ -158,7 +141,7 @@ static void test_cache_erase(size_t megabytes)
set.insert(hashes_insert_copy[i]);
/** Erase the first quarter */
for (uint32_t i = 0; i < (n_insert / 4); ++i)
- set.contains(hashes[i], true);
+ BOOST_CHECK(set.contains(hashes[i], true));
/** Insert the second half */
for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
set.insert(hashes_insert_copy[i]);
@@ -198,7 +181,7 @@ template <typename Cache>
static void test_cache_erase_parallel(size_t megabytes)
{
double load = 1;
- local_rand_ctx = FastRandomContext(true);
+ SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -208,7 +191,7 @@ static void test_cache_erase_parallel(size_t megabytes)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
for (uint8_t j = 0; j < 8; ++j)
- *(ptr++) = local_rand_ctx.rand32();
+ *(ptr++) = InsecureRand32();
}
/** We make a copy of the hashes because future optimizations of the
* cuckoocache may overwrite the inserted element, so the test is
@@ -237,8 +220,10 @@ static void test_cache_erase_parallel(size_t megabytes)
size_t ntodo = (n_insert/4)/3;
size_t start = ntodo*x;
size_t end = ntodo*(x+1);
- for (uint32_t i = start; i < end; ++i)
- set.contains(hashes[i], true);
+ for (uint32_t i = start; i < end; ++i) {
+ bool contains = set.contains(hashes[i], true);
+ assert(contains);
+ }
});
/** Wait for all threads to finish
@@ -300,7 +285,7 @@ static void test_cache_generations()
// iterations with non-deterministic values, so it isn't "overfit" to the
// specific entropy in FastRandomContext(true) and implementation of the
// cache.
- local_rand_ctx = FastRandomContext(true);
+ SeedInsecureRand(true);
// block_activity models a chunk of network activity. n_insert elements are
// added to the cache. The first and last n/4 are stored for removal later
@@ -317,7 +302,7 @@ static void test_cache_generations()
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)inserts[i].begin();
for (uint8_t j = 0; j < 8; ++j)
- *(ptr++) = local_rand_ctx.rand32();
+ *(ptr++) = InsecureRand32();
}
for (uint32_t i = 0; i < n_insert / 4; ++i)
reads.push_back(inserts[i]);
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 1034d4ade2..94e8c95345 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
// Remove key3 before it's even been written
batch.Erase(key3);
- dbw.WriteBatch(batch);
+ BOOST_CHECK(dbw.WriteBatch(batch));
BOOST_CHECK(dbw.Read(key, res));
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 97cf5ed345..8cf614bc8d 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// Test starts here
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1); // should result in getheaders
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
}
{
LOCK2(cs_main, dummyNode1.cs_vSend);
@@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(nStartTime+21*60);
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1); // should result in getheaders
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
}
{
LOCK2(cs_main, dummyNode1.cs_vSend);
@@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(nStartTime+24*60);
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1); // should result in disconnect
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
}
BOOST_CHECK(dummyNode1.fDisconnect == true);
SetMockTime(0);
@@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
{
- CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
+ CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
CNode &node = *vNodes.back();
node.SetSendVersion(PROTOCOL_VERSION);
@@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
}
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(connman->IsBanned(addr1));
BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
@@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
}
{
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode2);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
@@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
}
{
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode2);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(connman->IsBanned(addr2));
@@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
}
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(!connman->IsBanned(addr1));
{
@@ -270,7 +270,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
}
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(!connman->IsBanned(addr1));
{
@@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
}
{
LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(connman->IsBanned(addr1));
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
@@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
}
{
LOCK2(cs_main, dummyNode.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode);
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
}
BOOST_CHECK(connman->IsBanned(addr));
@@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
CKey key;
key.MakeNewKey(true);
CBasicKeyStore keystore;
- keystore.AddKey(key);
+ BOOST_CHECK(keystore.AddKey(key));
// 50 orphan transactions:
for (int i = 0; i < 50; i++)
@@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
- SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL);
+ BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
AddOrphanTx(MakeTransactionRef(tx), i);
}
@@ -386,7 +386,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[j].prevout.n = j;
tx.vin[j].prevout.hash = txPrev->GetHash();
}
- SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL);
+ BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
// Re-use same signature for other inputs
// (they don't have to be valid for this test)
for (unsigned int j = 1; j < tx.vin.size(); j++)
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 0e98f5a826..8da8cfc00c 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -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);
@@ -123,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/key_io_tests.cpp b/src/test/key_io_tests.cpp
index 5db62f4bba..1abaabd658 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_gen)
} else {
CTxDestination dest;
CScript exp_script(exp_payload.begin(), exp_payload.end());
- ExtractDestination(exp_script, dest);
+ BOOST_CHECK(ExtractDestination(exp_script, dest));
std::string address = EncodeDestination(dest);
BOOST_CHECK_EQUAL(address, exp_base58string);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 91cafd05d9..a768b4bcbd 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(key_signature_tests)
for (int i = 1; i <=20; ++i) {
sig.clear();
- key.Sign(msg_hash, sig, false, i);
+ BOOST_CHECK(key.Sign(msg_hash, sig, false, i));
found = sig[3] == 0x21 && sig[4] == 0x00;
if (found) {
break;
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(key_signature_tests)
sig.clear();
std::string msg = "A message to be signed" + std::to_string(i);
msg_hash = Hash(msg.begin(), msg.end());
- key.Sign(msg_hash, sig);
+ BOOST_CHECK(key.Sign(msg_hash, sig));
found = sig[3] == 0x20;
BOOST_CHECK(sig.size() <= 70);
found_small |= sig.size() < 70;
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index db38c9623c..2396aba0f1 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -59,13 +59,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
// Nothing in pool, remove should do nothing:
unsigned int poolSize = testPool.size();
- testPool.removeRecursive(txParent);
+ testPool.removeRecursive(CTransaction(txParent));
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Just the parent:
testPool.addUnchecked(entry.FromTx(txParent));
poolSize = testPool.size();
- testPool.removeRecursive(txParent);
+ testPool.removeRecursive(CTransaction(txParent));
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
// Parent, children, grandchildren:
@@ -77,18 +77,18 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
}
// Remove Child[0], GrandChild[0] should be removed:
poolSize = testPool.size();
- testPool.removeRecursive(txChild[0]);
+ testPool.removeRecursive(CTransaction(txChild[0]));
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
// ... make sure grandchild and child are gone:
poolSize = testPool.size();
- testPool.removeRecursive(txGrandChild[0]);
+ testPool.removeRecursive(CTransaction(txGrandChild[0]));
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
poolSize = testPool.size();
- testPool.removeRecursive(txChild[0]);
+ testPool.removeRecursive(CTransaction(txChild[0]));
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Remove parent, all children/grandchildren should go:
poolSize = testPool.size();
- testPool.removeRecursive(txParent);
+ testPool.removeRecursive(CTransaction(txParent));
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
BOOST_CHECK_EQUAL(testPool.size(), 0U);
@@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
// put into the mempool (maybe because it is non-standard):
poolSize = testPool.size();
- testPool.removeRecursive(txParent);
+ testPool.removeRecursive(CTransaction(txParent));
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
BOOST_CHECK_EQUAL(testPool.size(), 0U);
}
@@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx2.vout[0].nValue = 2 * COIN;
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
- uint64_t tx2Size = GetVirtualTransactionSize(tx2);
+ uint64_t tx2Size = GetVirtualTransactionSize(CTransaction(tx2));
/* lowest fee */
CMutableTransaction tx3 = CMutableTransaction();
@@ -357,7 +357,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx6.vout.resize(1);
tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx6.vout[0].nValue = 20 * COIN;
- uint64_t tx6Size = GetVirtualTransactionSize(tx6);
+ uint64_t tx6Size = GetVirtualTransactionSize(CTransaction(tx6));
pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6U);
@@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx7.vout.resize(1);
tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx7.vout[0].nValue = 10 * COIN;
- uint64_t tx7Size = GetVirtualTransactionSize(tx7);
+ uint64_t tx7Size = GetVirtualTransactionSize(CTransaction(tx7));
/* set the fee to just below tx2's feerate when including ancestor */
CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
@@ -464,12 +464,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK(pool.exists(tx2.GetHash()));
BOOST_CHECK(pool.exists(tx3.GetHash()));
- pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits
+ pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
BOOST_CHECK(!pool.exists(tx1.GetHash()));
BOOST_CHECK(!pool.exists(tx2.GetHash()));
BOOST_CHECK(!pool.exists(tx3.GetHash()));
- CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2));
+ CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
CMutableTransaction tx4 = CMutableTransaction();
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index a7074a5e43..f3648e2eee 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -159,7 +159,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- mempool.removeRecursive(tx);
+ mempool.removeRecursive(CTransaction(tx));
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
@@ -441,9 +441,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
- BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+ BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
@@ -451,12 +451,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
- BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
+ BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
@@ -467,9 +467,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = chainActive.Tip()->nHeight + 1;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails
- BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
@@ -478,23 +478,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails
- BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
prevheights[0] = chainActive.Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
- BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
+ BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
- BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
- BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
+ BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
- BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 61e579ed38..8afe4b8a59 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -76,20 +76,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
// Test a AND b:
keys.assign(1,key[0]);
keys.push_back(key[1]);
- s = sign_multisig(a_and_b, keys, txTo[0], 0);
+ s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
BOOST_CHECK(VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
for (int i = 0; i < 4; i++)
{
keys.assign(1,key[i]);
- s = sign_multisig(a_and_b, keys, txTo[0], 0);
+ s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 1: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
keys.assign(1,key[1]);
keys.push_back(key[i]);
- s = sign_multisig(a_and_b, keys, txTo[0], 0);
+ s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 2: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
@@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
for (int i = 0; i < 4; i++)
{
keys.assign(1,key[i]);
- s = sign_multisig(a_or_b, keys, txTo[1], 0);
+ s = sign_multisig(a_or_b, keys, CTransaction(txTo[1]), 0);
if (i == 0 || i == 1)
{
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i));
@@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
{
keys.assign(1,key[i]);
keys.push_back(key[j]);
- s = sign_multisig(escrow, keys, txTo[2], 0);
+ s = sign_multisig(escrow, keys, CTransaction(txTo[2]), 0);
if (i < j && i < 3 && j < 3)
{
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 1: %d %d", i, j));
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(multisig_Sign)
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
- keystore.AddKey(key[i]);
+ BOOST_CHECK(keystore.AddKey(key[i]));
}
CScript a_and_b;
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(multisig_Sign)
for (int i = 0; i < 3; i++)
{
- BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
+ BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
}
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index c1ee231d8a..e7a3c96343 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -54,10 +54,10 @@ public:
s << nUBuckets;
CService serv;
- Lookup("252.1.1.1", serv, 7777, false);
+ BOOST_CHECK(Lookup("252.1.1.1", serv, 7777, false));
CAddress addr = CAddress(serv, NODE_NONE);
CNetAddr resolved;
- LookupHost("252.2.2.2", resolved, false);
+ BOOST_CHECK(LookupHost("252.2.2.2", resolved, false));
CAddrInfo info = CAddrInfo(addr, resolved);
s << info;
}
@@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port)
BOOST_CHECK(port == Params().GetDefaultPort());
// test set port
unsigned short altPort = 12345;
- gArgs.SoftSetArg("-port", std::to_string(altPort));
+ BOOST_CHECK(gArgs.SoftSetArg("-port", std::to_string(altPort)));
port = GetListenPort();
BOOST_CHECK(port == altPort);
}
@@ -94,16 +94,16 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
addrmanUncorrupted.MakeDeterministic();
CService addr1, addr2, addr3;
- Lookup("250.7.1.1", addr1, 8333, false);
- Lookup("250.7.2.2", addr2, 9999, false);
- Lookup("250.7.3.3", addr3, 9999, false);
+ BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
+ BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
+ BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));
// Add three addresses to new table.
CService source;
- Lookup("252.5.1.1", source, 8333, false);
- addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
- addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
- addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);
+ BOOST_CHECK(Lookup("252.5.1.1", source, 8333, false));
+ BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source));
+ BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source));
+ BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source));
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
@@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
CAddrMan addrman2;
CAddrDB adb;
BOOST_CHECK(addrman2.size() == 0);
- adb.Read(addrman2, ssPeers2);
+ BOOST_CHECK(adb.Read(addrman2, ssPeers2));
BOOST_CHECK(addrman2.size() == 3);
}
@@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
CAddrMan addrman2;
CAddrDB adb;
BOOST_CHECK(addrman2.size() == 0);
- adb.Read(addrman2, ssPeers2);
+ BOOST_CHECK(!adb.Read(addrman2, ssPeers2));
BOOST_CHECK(addrman2.size() == 0);
}
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 51668cbe64..431b16cfc2 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].scriptSig = garbage;
tx.vout.resize(1);
tx.vout[0].nValue=0LL;
- CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx));
+ CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)));
// Create a fake block
std::vector<CTransactionRef> block;
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index c488d3edcf..7341389208 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -189,8 +189,8 @@ public:
prevector_tester() {
SeedInsecureRand();
- rand_seed = insecure_rand_seed;
- rand_cache = insecure_rand_ctx;
+ rand_seed = InsecureRand256();
+ rand_cache = FastRandomContext(rand_seed);
}
};
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 679e857ce6..1057d09471 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -38,11 +38,18 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50));
// Check that a nondeterministic ones are not
- FastRandomContext ctx3;
- FastRandomContext ctx4;
- BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
- BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
- BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
+ {
+ FastRandomContext ctx3, ctx4;
+ BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
+ }
+ {
+ FastRandomContext ctx3, ctx4;
+ BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
+ }
+ {
+ FastRandomContext ctx3, ctx4;
+ BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
+ }
}
BOOST_AUTO_TEST_CASE(fastrandom_randbits)
@@ -75,8 +82,42 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
for (int j = 1; j <= 10; ++j) {
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
}
+ Shuffle(test.begin(), test.end(), ctx);
+ for (int j = 1; j <= 10; ++j) {
+ BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
+ }
}
}
+/** Test that Shuffle reaches every permutation with equal probability. */
+BOOST_AUTO_TEST_CASE(shuffle_stat_test)
+{
+ FastRandomContext ctx(true);
+ uint32_t counts[5 * 5 * 5 * 5 * 5] = {0};
+ for (int i = 0; i < 12000; ++i) {
+ int data[5] = {0, 1, 2, 3, 4};
+ Shuffle(std::begin(data), std::end(data), ctx);
+ int pos = data[0] + data[1] * 5 + data[2] * 25 + data[3] * 125 + data[4] * 625;
+ ++counts[pos];
+ }
+ unsigned int sum = 0;
+ double chi_score = 0.0;
+ for (int i = 0; i < 5 * 5 * 5 * 5 * 5; ++i) {
+ int i1 = i % 5, i2 = (i / 5) % 5, i3 = (i / 25) % 5, i4 = (i / 125) % 5, i5 = i / 625;
+ uint32_t count = counts[i];
+ if (i1 == i2 || i1 == i3 || i1 == i4 || i1 == i5 || i2 == i3 || i2 == i4 || i2 == i5 || i3 == i4 || i3 == i5 || i4 == i5) {
+ BOOST_CHECK(count == 0);
+ } else {
+ chi_score += ((count - 100.0) * (count - 100.0)) / 100.0;
+ BOOST_CHECK(count > 50);
+ BOOST_CHECK(count < 150);
+ sum += count;
+ }
+ }
+ BOOST_CHECK(chi_score > 58.1411); // 99.9999% confidence interval
+ BOOST_CHECK(chi_score < 210.275);
+ BOOST_CHECK_EQUAL(sum, 12000);
+}
+
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/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 5b10935302..1556b2f667 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(sign)
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
- keystore.AddKey(key[i]);
+ BOOST_CHECK(keystore.AddKey(key[i]));
}
// 8 Scripts: checking all combinations of
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(sign)
CScript evalScripts[4];
for (int i = 0; i < 4; i++)
{
- keystore.AddCScript(standardScripts[i]);
+ BOOST_CHECK(keystore.AddCScript(standardScripts[i]));
evalScripts[i] = GetScriptForDestination(CScriptID(standardScripts[i]));
}
@@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(sign)
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
txFrom.vout[i+4].nValue = COIN;
}
- BOOST_CHECK(IsStandardTx(txFrom, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));
CMutableTransaction txTo[8]; // Spending transactions
for (int i = 0; i < 8; i++)
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(sign)
}
for (int i = 0; i < 8; i++)
{
- BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
+ BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(sign)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
- bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
+ bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
@@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(set)
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
- keystore.AddKey(key[i]);
+ BOOST_CHECK(keystore.AddKey(key[i]));
keys.push_back(key[i].GetPubKey());
}
@@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(set)
for (int i = 0; i < 4; i++)
{
outer[i] = GetScriptForDestination(CScriptID(inner[i]));
- keystore.AddCScript(inner[i]);
+ BOOST_CHECK(keystore.AddCScript(inner[i]));
}
CMutableTransaction txFrom; // Funding transaction:
@@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(set)
txFrom.vout[i].scriptPubKey = outer[i];
txFrom.vout[i].nValue = CENT;
}
- BOOST_CHECK(IsStandardTx(txFrom, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));
CMutableTransaction txTo[4]; // Spending transactions
for (int i = 0; i < 4; i++)
@@ -199,8 +199,8 @@ BOOST_AUTO_TEST_CASE(set)
}
for (int i = 0; i < 4; i++)
{
- BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
- BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i));
+ BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
+ BOOST_CHECK_MESSAGE(IsStandardTx(CTransaction(txTo[i]), reason), strprintf("txTo[%d].IsStandard", i));
}
}
@@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
for (int i = 0; i < 6; i++)
{
key[i].MakeNewKey(true);
- keystore.AddKey(key[i]);
+ BOOST_CHECK(keystore.AddKey(key[i]));
}
for (int i = 0; i < 3; i++)
keys.push_back(key[i].GetPubKey());
@@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
// First three are standard:
CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID());
- keystore.AddCScript(pay1);
+ BOOST_CHECK(keystore.AddCScript(pay1));
CScript pay1of3 = GetScriptForMultisig(1, keys);
txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG)
@@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY;
oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIG;
- keystore.AddCScript(oneAndTwo);
+ BOOST_CHECK(keystore.AddCScript(oneAndTwo));
txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo));
txFrom.vout[3].nValue = 4000;
@@ -302,21 +302,21 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
for (unsigned i = 0; i < MAX_P2SH_SIGOPS; i++)
fifteenSigops << ToByteVector(key[i%3].GetPubKey());
fifteenSigops << OP_15 << OP_CHECKMULTISIG;
- keystore.AddCScript(fifteenSigops);
+ BOOST_CHECK(keystore.AddCScript(fifteenSigops));
txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
txFrom.vout[4].nValue = 5000;
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
- keystore.AddCScript(sixteenSigops);
+ BOOST_CHECK(keystore.AddCScript(sixteenSigops));
txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(sixteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
- keystore.AddCScript(twentySigops);
+ BOOST_CHECK(keystore.AddCScript(twentySigops));
txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
txFrom.vout[6].nValue = 6000;
- AddCoins(coins, txFrom, 0);
+ AddCoins(coins, CTransaction(txFrom), 0);
CMutableTransaction txTo;
txTo.vout.resize(1);
@@ -328,18 +328,18 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txTo.vin[i].prevout.n = i;
txTo.vin[i].prevout.hash = txFrom.GetHash();
}
- BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL));
- BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1, SIGHASH_ALL));
- BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2, SIGHASH_ALL));
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 1, SIGHASH_ALL));
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 2, SIGHASH_ALL));
// SignSignature doesn't know how to sign these. We're
// not testing validating signatures, so just create
// dummy signatures that DO include the correct P2SH scripts:
txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
- BOOST_CHECK(::AreInputsStandard(txTo, coins));
+ BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins));
// 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
- BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U);
+ BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txTo), coins), 22U);
CMutableTransaction txToNonStd1;
txToNonStd1.vout.resize(1);
@@ -350,8 +350,8 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
- BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins));
- BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U);
+ BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins));
+ BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd1), coins), 16U);
CMutableTransaction txToNonStd2;
txToNonStd2.vout.resize(1);
@@ -362,8 +362,8 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd2.vin[0].scriptSig << std::vector<unsigned char>(twentySigops.begin(), twentySigops.end());
- BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins));
- BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U);
+ BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins));
+ BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index b3e4b12918..bde82018c7 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has key
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has key
- keystore.AddKey(uncompressedKey);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -428,7 +428,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has key
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has key
- keystore.AddKey(uncompressedKey);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -460,12 +460,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has redeemScript but no key
- keystore.AddCScript(redeemScript);
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has redeemScript and key
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -478,10 +478,10 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner));
scriptPubKey = GetScriptForDestination(CScriptID(redeemscript));
- keystore.AddCScript(redeemscript);
- keystore.AddCScript(redeemscript_inner);
- keystore.AddCScript(scriptPubKey);
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddCScript(redeemscript));
+ BOOST_CHECK(keystore.AddCScript(redeemscript_inner));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -494,10 +494,10 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
- keystore.AddCScript(witnessscript);
- keystore.AddCScript(redeemscript);
- keystore.AddCScript(scriptPubKey);
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(redeemscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -509,9 +509,9 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
- keystore.AddCScript(witnessscript);
- keystore.AddCScript(scriptPubKey);
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -524,10 +524,10 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
- keystore.AddCScript(witnessscript_inner);
- keystore.AddCScript(witnessscript);
- keystore.AddCScript(scriptPubKey);
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddCScript(witnessscript_inner));
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -535,12 +535,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2WPKH compressed
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
// Keystore implicitly has key and P2SH redeemScript
- keystore.AddCScript(scriptPubKey);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -548,7 +548,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2WPKH uncompressed
{
CBasicKeyStore keystore;
- keystore.AddKey(uncompressedKey);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID()));
@@ -557,7 +557,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has key and P2SH redeemScript
- keystore.AddCScript(scriptPubKey);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -573,19 +573,19 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has 1/2 keys
- keystore.AddKey(uncompressedKey);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has 2/2 keys
- keystore.AddKey(keys[1]);
+ BOOST_CHECK(keystore.AddKey(keys[1]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has 2/2 keys and the script
- keystore.AddCScript(scriptPubKey);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
@@ -594,8 +594,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2SH multisig
{
CBasicKeyStore keystore;
- keystore.AddKey(uncompressedKey);
- keystore.AddKey(keys[1]);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
@@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has redeemScript
- keystore.AddCScript(redeemScript);
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -613,8 +613,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2WSH multisig with compressed keys
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
- keystore.AddKey(keys[1]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
@@ -624,12 +624,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has keys and witnessScript, but no P2SH redeemScript
- keystore.AddCScript(witnessScript);
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has keys, witnessScript, P2SH redeemScript
- keystore.AddCScript(scriptPubKey);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -637,8 +637,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2WSH multisig with uncompressed key
{
CBasicKeyStore keystore;
- keystore.AddKey(uncompressedKey);
- keystore.AddKey(keys[1]);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
@@ -648,12 +648,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has keys and witnessScript, but no P2SH redeemScript
- keystore.AddCScript(witnessScript);
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has keys, witnessScript, P2SH redeemScript
- keystore.AddCScript(scriptPubKey);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
@@ -671,14 +671,14 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has witnessScript and P2SH redeemScript, but no keys
- keystore.AddCScript(redeemScript);
- keystore.AddCScript(witnessScript);
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
// Keystore has keys, witnessScript, P2SH redeemScript
- keystore.AddKey(keys[0]);
- keystore.AddKey(keys[1]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
}
@@ -686,7 +686,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// OP_RETURN
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
scriptPubKey.clear();
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
@@ -698,7 +698,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// witness unspendable
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
scriptPubKey.clear();
scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
@@ -710,7 +710,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// witness unknown
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
scriptPubKey.clear();
scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
@@ -722,7 +722,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// Nonstandard
{
CBasicKeyStore keystore;
- keystore.AddKey(keys[0]);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
scriptPubKey.clear();
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 1c70fdcce6..1b394753ef 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1030,6 +1030,28 @@ BOOST_AUTO_TEST_CASE(script_PushData)
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(pushdata4, pushdata4 + sizeof(pushdata4)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata4Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
+
+ const std::vector<unsigned char> pushdata1_trunc{OP_PUSHDATA1, 1};
+ const std::vector<unsigned char> pushdata2_trunc{OP_PUSHDATA2, 1, 0};
+ const std::vector<unsigned char> pushdata4_trunc{OP_PUSHDATA4, 1, 0, 0, 0};
+
+ std::vector<std::vector<unsigned char>> stack_ignore;
+ BOOST_CHECK(!EvalScript(stack_ignore, CScript(pushdata1_trunc.begin(), pushdata1_trunc.end()), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_BAD_OPCODE);
+ BOOST_CHECK(!EvalScript(stack_ignore, CScript(pushdata2_trunc.begin(), pushdata2_trunc.end()), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_BAD_OPCODE);
+ BOOST_CHECK(!EvalScript(stack_ignore, CScript(pushdata4_trunc.begin(), pushdata4_trunc.end()), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_BAD_OPCODE);
+}
+
+BOOST_AUTO_TEST_CASE(script_cltv_truncated)
+{
+ const auto script_cltv_trunc = CScript() << OP_CHECKLOCKTIMEVERIFY;
+
+ std::vector<std::vector<unsigned char>> stack_ignore;
+ ScriptError err;
+ BOOST_CHECK(!EvalScript(stack_ignore, script_cltv_trunc, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_INVALID_STACK_OPERATION);
}
static CScript
@@ -1078,18 +1100,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
const CTransaction txFrom12{BuildCreditingTransaction(scriptPubKey12)};
CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom12);
- CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
+ CScript goodsig1 = sign_multisig(scriptPubKey12, key1, CTransaction(txTo12));
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
txTo12.vout[0].nValue = 2;
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
- CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
+ CScript goodsig2 = sign_multisig(scriptPubKey12, key2, CTransaction(txTo12));
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
- CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
+ CScript badsig1 = sign_multisig(scriptPubKey12, key3, CTransaction(txTo12));
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
@@ -1111,54 +1133,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2);
- CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript goodsig1 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key1); keys.push_back(key3);
- CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript goodsig2 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key3);
- CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript goodsig3 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
- CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig1 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
- CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig2 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
- CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig3 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
- CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig4 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
- CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig5 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear(); // Must have signatures
- CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
+ CScript badsig6 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}
@@ -1185,11 +1207,11 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
key.MakeNewKey(i%2 == 1);
keys.push_back(key);
pubkeys.push_back(key.GetPubKey());
- keystore.AddKey(key);
+ BOOST_CHECK(keystore.AddKey(key));
}
CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
- CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom);
+ CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), CTransaction(txFrom));
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
SignatureData scriptSig;
@@ -1198,7 +1220,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(combined.scriptSig.empty());
// Single signature case:
- SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); // changes scriptSig
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL)); // changes scriptSig
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
@@ -1206,31 +1228,31 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
SignatureData scriptSigCopy = scriptSig;
// Signing again will give a different, valid signature:
- SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig);
BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig);
// P2SH, single-signature case:
CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG;
- keystore.AddCScript(pkSingle);
+ BOOST_CHECK(keystore.AddCScript(pkSingle));
scriptPubKey = GetScriptForDestination(CScriptID(pkSingle));
- SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig);
BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
scriptSigCopy = scriptSig;
- SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig);
BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig);
// Hardest case: Multisig 2-of-3
scriptPubKey = GetScriptForMultisig(2, pubkeys);
- keystore.AddCScript(scriptPubKey);
- SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
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..04d5462acb 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());
@@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
int nIn = InsecureRandRange(txTo.vin.size());
uint256 sh, sho;
- sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
+ sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType);
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE);
#if defined(PRINT_SIGHASH_JSON)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 5462fea777..6a0349cd4e 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -102,7 +102,7 @@ static void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CM
spendingTx.vout[0].nValue = 1;
spendingTx.vout[0].scriptPubKey = CScript();
- AddCoins(coins, creationTx, 0);
+ AddCoins(coins, CTransaction(creationTx), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
@@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// is not accurate.
assert(GetTransactionSigOpCost(CTransaction(creationTx), coins, flags) == MAX_PUBKEYS_PER_MULTISIG * WITNESS_SCALE_FACTOR);
// Sanity check: script verification fails because of an invalid signature.
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
// Multisig nested in P2SH
@@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2 * WITNESS_SCALE_FACTOR);
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
// P2WPKH witness program
@@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
// No signature operations if we don't verify the witness.
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
// The sig op cost for witness version != 0 is zero.
assert(scriptPubKey[0] == 0x00);
@@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
}
// P2WSH witness program
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
// P2WSH nested in P2SH
@@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
- assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
+ assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index f7874e6882..a3201de385 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -11,6 +11,7 @@
#include <crypto/sha256.h>
#include <miner.h>
#include <net_processing.h>
+#include <noui.h>
#include <pow.h>
#include <rpc/register.h>
#include <rpc/server.h>
@@ -21,6 +22,8 @@
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
+FastRandomContext g_insecure_rand_ctx;
+
void CConnmanTest::AddNode(CNode& node)
{
LOCK(g_connman->cs_vNodes);
@@ -36,12 +39,6 @@ void CConnmanTest::ClearNodes()
g_connman->vNodes.clear();
}
-uint256 insecure_rand_seed = GetRandHash();
-FastRandomContext insecure_rand_ctx(insecure_rand_seed);
-
-extern bool fPrintToConsole;
-extern void noui_connect();
-
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
os << num.ToString();
@@ -118,16 +115,16 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
TestingSetup::~TestingSetup()
{
- threadGroup.interrupt_all();
- threadGroup.join_all();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
- g_connman.reset();
- peerLogic.reset();
- UnloadBlockIndex();
- pcoinsTip.reset();
- pcoinsdbview.reset();
- pblocktree.reset();
+ threadGroup.interrupt_all();
+ threadGroup.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+ g_connman.reset();
+ peerLogic.reset();
+ UnloadBlockIndex();
+ pcoinsTip.reset();
+ pcoinsdbview.reset();
+ pblocktree.reset();
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 3872767133..31d90c0151 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -15,27 +15,36 @@
#include <txmempool.h>
#include <memory>
+#include <type_traits>
#include <boost/thread.hpp>
-extern uint256 insecure_rand_seed;
-extern FastRandomContext insecure_rand_ctx;
+// Enable BOOST_CHECK_EQUAL for enum class types
+template <typename T>
+std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
+{
+ return stream << static_cast<typename std::underlying_type<T>::type>(e);
+}
+
+/**
+ * This global and the helpers that use it are not thread-safe.
+ *
+ * If thread-safety is needed, the global could be made thread_local (given
+ * that thread_local is supported on all architectures we support) or a
+ * per-thread instance could be used in the multi-threaded test.
+ */
+extern FastRandomContext g_insecure_rand_ctx;
-static inline void SeedInsecureRand(bool fDeterministic = false)
+static inline void SeedInsecureRand(bool deterministic = false)
{
- if (fDeterministic) {
- insecure_rand_seed = uint256();
- } else {
- insecure_rand_seed = GetRandHash();
- }
- insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
+ g_insecure_rand_ctx = FastRandomContext(deterministic);
}
-static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); }
-static inline uint256 InsecureRand256() { return insecure_rand_ctx.rand256(); }
-static inline uint64_t InsecureRandBits(int bits) { return insecure_rand_ctx.randbits(bits); }
-static inline uint64_t InsecureRandRange(uint64_t range) { return insecure_rand_ctx.randrange(range); }
-static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); }
+static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); }
+static inline uint256 InsecureRand256() { return g_insecure_rand_ctx.rand256(); }
+static inline uint64_t InsecureRandBits(int bits) { return g_insecure_rand_ctx.randbits(bits); }
+static inline uint64_t InsecureRandRange(uint64_t range) { return g_insecure_rand_ctx.randrange(range); }
+static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); }
static constexpr CAmount CENT{1000000};
@@ -65,7 +74,7 @@ struct CConnmanTest {
};
class PeerLogicValidation;
-struct TestingSetup: public BasicTestingSetup {
+struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 9978c71661..c68b6bbb4d 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -273,11 +273,11 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
CMutableTransaction tx;
stream >> tx;
CValidationState state;
- BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid.");
+ BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid.");
// Check that duplicate txins fail
tx.vin.push_back(tx.vin[0]);
- BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
+ BOOST_CHECK_MESSAGE(!CheckTransaction(CTransaction(tx), state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
}
//
@@ -306,14 +306,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
dummyTransactions[0].vout[1].nValue = 50*CENT;
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- AddCoins(coinsRet, dummyTransactions[0], 0);
+ AddCoins(coinsRet, CTransaction(dummyTransactions[0]), 0);
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
dummyTransactions[1].vout[1].nValue = 22*CENT;
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
- AddCoins(coinsRet, dummyTransactions[1], 0);
+ AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
}
@@ -340,8 +340,8 @@ BOOST_AUTO_TEST_CASE(test_Get)
t1.vout[0].nValue = 90*CENT;
t1.vout[0].scriptPubKey << OP_1;
- BOOST_CHECK(AreInputsStandard(t1, coins));
- BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
+ BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins));
+ BOOST_CHECK_EQUAL(coins.GetValueIn(CTransaction(t1)), (50+21+22)*CENT);
}
static void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
@@ -421,7 +421,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
CKey key;
key.MakeNewKey(true); // Need to use compressed keys in segwit or the signing will fail
CBasicKeyStore keystore;
- keystore.AddKeyPubKey(key, key.GetPubKey());
+ BOOST_CHECK(keystore.AddKeyPubKey(key, key.GetPubKey()));
CKeyID hash = key.GetPubKey().GetID();
CScript scriptPubKey = CScript() << OP_0 << std::vector<unsigned char>(hash.begin(), hash.end());
@@ -518,10 +518,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
pubkey3 = key3.GetPubKey();
pubkey1L = key1L.GetPubKey();
pubkey2L = key2L.GetPubKey();
- keystore.AddKeyPubKey(key1, pubkey1);
- keystore.AddKeyPubKey(key2, pubkey2);
- keystore.AddKeyPubKey(key1L, pubkey1L);
- keystore.AddKeyPubKey(key2L, pubkey2L);
+ BOOST_CHECK(keystore.AddKeyPubKey(key1, pubkey1));
+ BOOST_CHECK(keystore.AddKeyPubKey(key2, pubkey2));
+ BOOST_CHECK(keystore.AddKeyPubKey(key1L, pubkey1L));
+ BOOST_CHECK(keystore.AddKeyPubKey(key2L, pubkey2L));
CScript scriptPubkey1, scriptPubkey2, scriptPubkey1L, scriptPubkey2L, scriptMulti;
scriptPubkey1 << ToByteVector(pubkey1) << OP_CHECKSIG;
scriptPubkey2 << ToByteVector(pubkey2) << OP_CHECKSIG;
@@ -531,19 +531,19 @@ BOOST_AUTO_TEST_CASE(test_witness)
oneandthree.push_back(pubkey1);
oneandthree.push_back(pubkey3);
scriptMulti = GetScriptForMultisig(2, oneandthree);
- keystore.AddCScript(scriptPubkey1);
- keystore.AddCScript(scriptPubkey2);
- keystore.AddCScript(scriptPubkey1L);
- keystore.AddCScript(scriptPubkey2L);
- keystore.AddCScript(scriptMulti);
- keystore.AddCScript(GetScriptForWitness(scriptPubkey1));
- keystore.AddCScript(GetScriptForWitness(scriptPubkey2));
- keystore.AddCScript(GetScriptForWitness(scriptPubkey1L));
- keystore.AddCScript(GetScriptForWitness(scriptPubkey2L));
- keystore.AddCScript(GetScriptForWitness(scriptMulti));
- keystore2.AddCScript(scriptMulti);
- keystore2.AddCScript(GetScriptForWitness(scriptMulti));
- keystore2.AddKeyPubKey(key3, pubkey3);
+ BOOST_CHECK(keystore.AddCScript(scriptPubkey1));
+ BOOST_CHECK(keystore.AddCScript(scriptPubkey2));
+ BOOST_CHECK(keystore.AddCScript(scriptPubkey1L));
+ BOOST_CHECK(keystore.AddCScript(scriptPubkey2L));
+ BOOST_CHECK(keystore.AddCScript(scriptMulti));
+ BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey1)));
+ BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey2)));
+ BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey1L)));
+ BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey2L)));
+ BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptMulti)));
+ BOOST_CHECK(keystore2.AddCScript(scriptMulti));
+ BOOST_CHECK(keystore2.AddCScript(GetScriptForWitness(scriptMulti)));
+ BOOST_CHECK(keystore2.AddKeyPubKey(key3, pubkey3));
CTransactionRef output1, output2;
CMutableTransaction input1, input2;
@@ -697,75 +697,75 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
std::string reason;
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Check dust with default relay fee:
CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// not dust:
t.vout[0].nValue = nDustThreshold;
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 3702 / 1000
dustRelayFee = CFeeRate(3702);
// dust:
t.vout[0].nValue = 673 - 1;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// not dust:
t.vout[0].nValue = 673;
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
t.vout[0].scriptPubKey = CScript() << OP_1;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// Data payload can be encoded in any way...
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01");
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()!
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0 << ParseHex("01") << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16;
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// ...so long as it only contains PUSHDATA's
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// TX_NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
- BOOST_CHECK(IsStandardTx(t, reason));
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Only one TX_NULL_DATA permitted in all cases
t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
- BOOST_CHECK(!IsStandardTx(t, reason));
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 506a60d173..c467f27836 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -156,8 +156,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
CBasicKeyStore keystore;
- keystore.AddKey(coinbaseKey);
- keystore.AddCScript(p2pk_scriptPubKey);
+ BOOST_CHECK(keystore.AddKey(coinbaseKey));
+ BOOST_CHECK(keystore.AddCScript(p2pk_scriptPubKey));
// flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing
@@ -198,20 +198,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
- BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputs returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -238,7 +238,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true);
}
// Test CHECKLOCKTIMEVERIFY
@@ -261,13 +261,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx);
- BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -289,13 +289,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx);
- BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -314,15 +314,15 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Sign
SignatureData sigdata;
- ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata);
+ BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata));
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true);
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true);
}
{
@@ -342,12 +342,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Sign
for (int i=0; i<2; ++i) {
SignatureData sigdata;
- ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata);
+ BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata));
UpdateInput(tx.vin[i], sigdata);
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(tx, 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true);
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -357,12 +357,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 4316f37999..62dfed6c41 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -104,8 +104,8 @@ void BuildChain(const uint256& root, int height, const unsigned int invalid_rate
{
if (height <= 0 || blocks.size() >= max_size) return;
- bool gen_invalid = GetRand(100) < invalid_rate;
- bool gen_fork = GetRand(100) < branch_rate;
+ bool gen_invalid = InsecureRandRange(100) < invalid_rate;
+ bool gen_fork = InsecureRandRange(100) < branch_rate;
const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
blocks.push_back(pblock);
@@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params()));
// Connect the genesis block and drain any outstanding events
- ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored);
+ BOOST_CHECK(ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -152,12 +152,13 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
// create a bunch of threads that repeatedly process a block generated above at random
// this will create parallelism and randomness inside validation - the ValidationInterface
// will subscribe to events generated during block validation and assert on ordering invariance
- boost::thread_group threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
- threads.create_thread([&blocks]() {
+ threads.emplace_back([&blocks]() {
bool ignored;
+ FastRandomContext insecure;
for (int i = 0; i < 1000; i++) {
- auto block = blocks[GetRand(blocks.size() - 1)];
+ auto block = blocks[insecure.randrange(blocks.size() - 1)];
ProcessNewBlock(Params(), block, true, &ignored);
}
@@ -171,7 +172,9 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
});
}
- threads.join_all();
+ for (auto& t : threads) {
+ t.join();
+ }
while (GetMainSignals().CallbacksPending() > 0) {
MilliSleep(100);
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 512a3619ca..5696684ed6 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2487,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
@@ -2777,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();
}
@@ -2838,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++;
@@ -2868,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) {
@@ -2946,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);
@@ -3267,6 +3267,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
int nLockTimeFlags = 0;
if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3819,8 +3820,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 +3838,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 +3852,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 +4221,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 +4522,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 +4535,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/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 5e955b8495..8a37f374a1 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -223,7 +223,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
std::vector<OutputGroup> applicable_groups;
CAmount nTotalLower = 0;
- random_shuffle(groups.begin(), groups.end(), GetRandInt);
+ Shuffle(groups.begin(), groups.end(), FastRandomContext());
for (const OutputGroup& group : groups) {
if (group.m_value == nTargetValue) {
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index d75e30d336..98e2abbd18 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -709,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/rpcdump.cpp b/src/wallet/rpcdump.cpp
index fa1e209bf2..647af3eb86 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -110,18 +110,14 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
RPCHelpMan{"importprivkey",
- "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\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",
{
- {"privkey", RPCArg::Type::STR, false},
- {"label", RPCArg::Type::STR, true},
- {"rescan", RPCArg::Type::BOOL, true},
+ {"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() +
- "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=current label if address exists, otherwise \"\") An optional label\n"
- "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
"\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"
@@ -278,17 +274,12 @@ UniValue importaddress(const JSONRPCRequest& request)
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, false},
- {"label", RPCArg::Type::STR, true},
- {"rescan", RPCArg::Type::BOOL, true},
- {"p2sh", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\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"
@@ -365,13 +356,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
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, false},
- {"txoutproof", RPCArg::Type::STR_HEX, false},
+ {"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() +
- "\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"
+ .ToString()
);
CMutableTransaction tx;
@@ -434,11 +422,9 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
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, false},
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded id of the transaction you are deleting"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
"\nExamples:\n"
+ HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
"\nAs a JSON-RPC call\n"
@@ -477,15 +463,11 @@ UniValue importpubkey(const JSONRPCRequest& request)
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, false},
- {"label", RPCArg::Type::STR, true},
- {"rescan", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\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"
@@ -555,11 +537,9 @@ UniValue importwallet(const JSONRPCRequest& request)
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, false},
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet file"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"filename\" (string, required) The wallet file\n"
"\nExamples:\n"
"\nDump the wallet\n"
+ HelpExampleCli("dumpwallet", "\"test\"") +
@@ -690,11 +670,9 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
- {"address", RPCArg::Type::STR, false},
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for the private key"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address for the private key\n"
"\nResult:\n"
"\"key\" (string) The private key\n"
"\nExamples:\n"
@@ -741,11 +719,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
"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, false},
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The filename with path (either absolute or relative to bitcoind)"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
"\nResult:\n"
"{ (json object)\n"
" \"filename\" : { (string) The filename with full absolute path\n"
@@ -1127,57 +1103,49 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
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\n",
+ "\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,
+ {"requests", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Data to be imported",
{
- {"", RPCArg::Type::OBJ,
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
{
- {
- {"scriptPubKey", RPCArg::Type::STR, false},
- {"timestamp", RPCArg::Type::NUM, false},
- {"redeemscript", RPCArg::Type::STR, true},
- {"witnessscript", RPCArg::Type::STR, true},
- {"internal", RPCArg::Type::BOOL, true},
- {"watchonly", RPCArg::Type::BOOL, true},
- {"label", RPCArg::Type::STR, true},
+ {"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"},
},
- false},
+ },
},
- false, "\"requests\""},
- {"options", RPCArg::Type::OBJ,
+ "\"requests\""},
+ {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
{
- {"rescan", RPCArg::Type::BOOL, true},
+ {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Stating if should rescan the blockchain after all imports"},
},
- true, "\"options\""},
+ "\"options\""},
}}
.ToString() +
- "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 aka change\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"
"\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" +
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index c9e1faddee..c1cdd0b2ee 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -155,13 +155,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
"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, true},
- {"address_type", RPCArg::Type::STR, true},
+ {"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() +
- "\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"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n"
@@ -219,11 +216,9 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
"\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, true},
+ {"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() +
- "\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"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n"
@@ -276,13 +271,10 @@ static UniValue setlabel(const JSONRPCRequest& request)
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
- {"address", RPCArg::Type::STR, false},
- {"label", RPCArg::Type::STR, false},
+ {"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() +
- "\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"
"\nExamples:\n"
+ HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")
@@ -359,35 +351,26 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
RPCHelpMan{"sendtoaddress",
- "\nSend an amount to a given address.\n",
+ "\nSend an amount to a given address." +
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"address", RPCArg::Type::STR, false},
- {"amount", RPCArg::Type::AMOUNT, false},
- {"comment", RPCArg::Type::STR, true},
- {"comment_to", RPCArg::Type::STR, true},
- {"subtractfeefromamount", RPCArg::Type::BOOL, true},
- {"replaceable", RPCArg::Type::BOOL, true},
- {"conf_target", RPCArg::Type::NUM, true},
- {"estimate_mode", RPCArg::Type::STR, true},
- }}
- .ToString() +
- 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"
+ {"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"
@@ -522,16 +505,13 @@ static UniValue signmessage(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
RPCHelpMan{"signmessage",
- "\nSign a message with the private key of an address",
+ "\nSign a message with the private key of an address" +
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"address", RPCArg::Type::STR, false},
- {"message", RPCArg::Type::STR, false},
+ {"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() +
- 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"
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
"\nExamples:\n"
@@ -593,13 +573,10 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
- {"address", RPCArg::Type::STR, false},
- {"minconf", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
"\nExamples:\n"
@@ -667,13 +644,10 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
- {"label", RPCArg::Type::STR, false},
- {"minconf", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
"\nExamples:\n"
@@ -741,15 +715,11 @@ static UniValue getbalance(const JSONRPCRequest& request)
"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, true},
- {"minconf", RPCArg::Type::NUM, true},
- {"include_watchonly", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
"\nExamples:\n"
@@ -824,50 +794,33 @@ static UniValue sendmany(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
RPCHelpMan{"sendmany",
- "\nSend multiple times. Amounts are double-precision floating point numbers.\n",
+ "\nSend multiple times. Amounts are double-precision floating point numbers." +
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"dummy", RPCArg::Type::STR, false, "\"\""},
- {"amounts", RPCArg::Type::OBJ,
- {
- {"address", RPCArg::Type::AMOUNT, false},
- },
- false},
- {"minconf", RPCArg::Type::NUM, true},
- {"comment", RPCArg::Type::STR, true},
- {"subtractfeefrom", RPCArg::Type::ARR,
+ {"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::STR, true},
+ {"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"},
},
- true},
- {"replaceable", RPCArg::Type::BOOL, true},
- {"conf_target", RPCArg::Type::NUM, true},
- {"estimate_mode", RPCArg::Type::STR, true},
- }}
- .ToString() +
- 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"
+ },
+ {"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"
@@ -995,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"
@@ -1238,17 +1192,12 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
- {"minconf", RPCArg::Type::NUM, true},
- {"include_empty", RPCArg::Type::BOOL, true},
- {"include_watchonly", RPCArg::Type::BOOL, true},
- {"address_filter", RPCArg::Type::STR, true},
+ {"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() +
- "\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"
"\nResult:\n"
"[\n"
" {\n"
@@ -1296,16 +1245,11 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
- {"minconf", RPCArg::Type::NUM, true},
- {"include_empty", RPCArg::Type::BOOL, true},
- {"include_watchonly", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
-
"\nResult:\n"
"[\n"
" {\n"
@@ -1442,18 +1386,13 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\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, true},
- {"count", RPCArg::Type::NUM, true},
- {"skip", RPCArg::Type::NUM, true},
- {"include_watchonly", RPCArg::Type::BOOL, true},
+ {"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() +
- "\nArguments:\n"
- "1. \"label\" (string, optional) 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.\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"
"\nResult:\n"
"[\n"
" {\n"
@@ -1582,18 +1521,13 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
"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, true},
- {"target_confirmations", RPCArg::Type::NUM, true},
- {"include_watchonly", RPCArg::Type::BOOL, true},
- {"include_removed", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
@@ -1732,13 +1666,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
- {"txid", RPCArg::Type::STR, false},
- {"include_watchonly", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n"
@@ -1841,11 +1772,9 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
"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, false},
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
@@ -1887,11 +1816,9 @@ static UniValue backupwallet(const JSONRPCRequest& request)
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
- {"destination", RPCArg::Type::STR, false},
+ {"destination", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The destination directory or file"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"destination\" (string) The destination directory or file\n"
"\nExamples:\n"
+ HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
@@ -1925,14 +1852,12 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
RPCHelpMan{"keypoolrefill",
- "\nFills the keypool.",
+ "\nFills the keypool."+
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"newsize", RPCArg::Type::NUM, true},
+ {"newsize", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "100", "The new keypool size"},
}}
.ToString() +
- HelpRequiringPassphrase(pwallet) + "\n"
- "\nArguments\n"
- "1. newsize (numeric, optional, default=100) The new keypool size\n"
"\nExamples:\n"
+ HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
@@ -1979,13 +1904,10 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
"\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, false},
- {"timeout", RPCArg::Type::NUM, false},
+ {"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() +
- "\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"
"\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"
@@ -2067,13 +1989,10 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
- {"oldpassphrase", RPCArg::Type::STR, false},
- {"newpassphrase", RPCArg::Type::STR, false},
+ {"oldpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The current passphrase"},
+ {"newpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The new passphrase"},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"oldpassphrase\" (string) The current passphrase\n"
- "2. \"newpassphrase\" (string) The new passphrase\n"
"\nExamples:\n"
+ HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
@@ -2170,11 +2089,9 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"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, false},
+ {"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() +
- "\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"
"\nExamples:\n"
"\nEncrypt your wallet\n"
+ HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
@@ -2233,30 +2150,19 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"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, false},
- {"transactions", RPCArg::Type::ARR,
+ {"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,
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"vout", RPCArg::Type::NUM, false},
+ {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
},
- true},
+ },
},
- true},
+ },
}}
.ToString() +
- "\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"
-
"\nResult:\n"
"true|false (boolean) Whether the command was successful or not\n"
@@ -2423,11 +2329,9 @@ static UniValue settxfee(const JSONRPCRequest& request)
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
{
- {"amount", RPCArg::Type::NUM, false},
+ {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The transaction fee in " + CURRENCY_UNIT + "/kB"},
}}
.ToString() +
- "\nArguments:\n"
- "1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n"
"\nResult\n"
"true|false (boolean) Returns true if successful\n"
"\nExamples:\n"
@@ -2599,11 +2503,9 @@ static UniValue loadwallet(const JSONRPCRequest& request)
"\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, false},
+ {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet directory or .dat file."},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"filename\" (string, required) The wallet directory or .dat file.\n"
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n"
@@ -2654,13 +2556,10 @@ static UniValue createwallet(const JSONRPCRequest& request)
RPCHelpMan{"createwallet",
"\nCreates and loads a new wallet.\n",
{
- {"wallet_name", RPCArg::Type::STR, false},
- {"disable_private_keys", RPCArg::Type::BOOL, true},
+ {"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() +
- "\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"
"\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"
@@ -2712,11 +2611,9 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
"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, true},
+ {"wallet_name", RPCArg::Type::STR, /* opt */ true, /* default_val */ "the wallet name from the RPC request", "The name of the wallet to unload."},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n"
"\nExamples:\n"
+ HelpExampleCli("unloadwallet", "wallet_name")
+ HelpExampleRpc("unloadwallet", "wallet_name")
@@ -2813,41 +2710,25 @@ static UniValue listunspent(const JSONRPCRequest& request)
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n",
{
- {"minconf", RPCArg::Type::NUM, true},
- {"maxconf", RPCArg::Type::NUM, true},
- {"addresses", RPCArg::Type::ARR,
+ {"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, true},
+ {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "bitcoin address"},
},
- true},
- {"include_unsafe", RPCArg::Type::BOOL, true},
- {"query_options", RPCArg::Type::OBJ,
+ },
+ {"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, true},
- {"maximumAmount", RPCArg::Type::AMOUNT, true},
- {"maximumCount", RPCArg::Type::NUM, true},
- {"minimumSumAmount", RPCArg::Type::AMOUNT, true},
+ {"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 + ""},
},
- true, "query_options"},
+ "query_options"},
}}
.ToString() +
- "\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"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
@@ -3130,56 +3011,36 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"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, false},
- {"options", RPCArg::Type::OBJ,
+ {"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, true},
- {"changePosition", RPCArg::Type::NUM, true},
- {"change_type", RPCArg::Type::STR, true},
- {"includeWatching", RPCArg::Type::BOOL, true},
- {"lockUnspents", RPCArg::Type::BOOL, true},
- {"feeRate", RPCArg::Type::AMOUNT, true},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR,
+ {"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"
+ " 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.",
{
- {"vout_index", RPCArg::Type::NUM, true},
+ {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
},
- true},
- {"replaceable", RPCArg::Type::BOOL, true},
- {"conf_target", RPCArg::Type::NUM, true},
- {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ {"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\""},
},
- true, "options"},
- {"iswitness", RPCArg::Type::BOOL, true},
+ "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() +
- "\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"
- " 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"
- " \"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"
-
"\nResult:\n"
"{\n"
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
@@ -3233,48 +3094,32 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
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.\n",
+ "this transaction depends on but may not yet be in the block chain." +
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"hexstring", RPCArg::Type::STR, false},
- {"prevtxs", RPCArg::Type::ARR,
+ {"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,
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"vout", RPCArg::Type::NUM, false},
- {"scriptPubKey", RPCArg::Type::STR_HEX, false},
- {"redeemScript", RPCArg::Type::STR_HEX, false},
- {"amount", RPCArg::Type::AMOUNT, false},
+ {"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"},
},
- false},
+ },
},
- true},
- {"sighashtype", RPCArg::Type::STR, true},
- }}
- .ToString() +
- 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"
+ },
+ {"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"
@@ -3335,38 +3180,29 @@ static UniValue bumpfee(const JSONRPCRequest& request)
"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, false},
- {"options", RPCArg::Type::OBJ,
+ {"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, true},
- {"totalFee", RPCArg::Type::AMOUNT, true},
- {"replaceable", RPCArg::Type::BOOL, true},
- {"estimate_mode", RPCArg::Type::STR, true},
- },
- true, "options"},
- }}
- .ToString() +
- "\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"
+ {"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"
@@ -3489,13 +3325,10 @@ UniValue generate(const JSONRPCRequest& request)
RPCHelpMan{"generate",
"\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n",
{
- {"nblocks", RPCArg::Type::NUM, false},
- {"maxtries", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
@@ -3546,13 +3379,10 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n",
{
- {"start_height", RPCArg::Type::NUM, true},
- {"stop_height", RPCArg::Type::NUM, true},
+ {"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() +
- "\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"
"\nResult:\n"
"{\n"
" \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n"
@@ -3607,16 +3437,17 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}
}
- CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true);
- if (!stopBlock) {
- if (pwallet->IsAbortingRescan()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
- }
- // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
- stopBlock = pindexStop ? pindexStop : pChainTip;
- }
- else {
+ const CBlockIndex *failed_block, *stopBlock;
+ CWallet::ScanResult result =
+ pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true);
+ switch (result) {
+ case CWallet::ScanResult::SUCCESS:
+ break; // stopBlock set by ScanForWalletTransactions
+ case CWallet::ScanResult::FAILURE:
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
+ case CWallet::ScanResult::USER_ABORT:
+ throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
+ // no default case, so the compiler can warn about missing cases
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", pindexStart->nHeight);
@@ -3758,11 +3589,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
"\nReturn information about the given bitcoin address. Some information requires the address\n"
"to be in the wallet.\n",
{
- {"address", RPCArg::Type::STR, false},
+ {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to get the information of."},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to get the information of.\n"
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
@@ -3888,11 +3717,9 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
- {"label", RPCArg::Type::STR, false},
+ {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label."},
}}
.ToString() +
- "\nArguments:\n"
- "1. \"label\" (string, required) The label.\n"
"\nResult:\n"
"{ (json object with addresses as keys)\n"
" \"address\": { (json object with information about address)\n"
@@ -3937,11 +3764,9 @@ static UniValue listlabels(const JSONRPCRequest& request)
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
- {"purpose", RPCArg::Type::STR, true},
+ {"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() +
- "\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"
"\nResult:\n"
"[ (json array of string)\n"
" \"label\", (string) Label name\n"
@@ -3995,20 +3820,17 @@ UniValue sethdseed(const JSONRPCRequest& request)
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.\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, true},
- {"seed", RPCArg::Type::STR, true},
- }}
- .ToString()
- + 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"
+ {"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")
@@ -4138,28 +3960,21 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
- "that we can sign for.\n",
+ "that we can sign for." +
+ HelpRequiringPassphrase(pwallet) + "\n",
{
- {"psbt", RPCArg::Type::STR, false},
- {"sign", RPCArg::Type::BOOL, true},
- {"sighashtype", RPCArg::Type::STR, true},
- {"bip32derivs", RPCArg::Type::BOOL, true},
- }}
- .ToString() +
- 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"
+ {"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"
@@ -4212,99 +4027,64 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\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,
+ {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
{
- {"", RPCArg::Type::OBJ,
+ {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
{
- {"txid", RPCArg::Type::STR_HEX, false},
- {"vout", RPCArg::Type::NUM, false},
- {"sequence", RPCArg::Type::NUM, false},
+ {"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"},
},
- false},
+ },
+ },
},
- false},
- {"outputs", RPCArg::Type::ARR,
+ {"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,
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"address", RPCArg::Type::AMOUNT, true},
+ {"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 + ""},
},
- true},
- {"", RPCArg::Type::OBJ,
+ },
+ {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
{
- {"data", RPCArg::Type::STR_HEX, true},
+ {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
},
- true},
+ },
},
- false},
- {"locktime", RPCArg::Type::NUM, true},
- {"options", RPCArg::Type::OBJ,
+ },
+ {"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, true},
- {"changePosition", RPCArg::Type::NUM, true},
- {"change_type", RPCArg::Type::STR, true},
- {"includeWatching", RPCArg::Type::BOOL, true},
- {"lockUnspents", RPCArg::Type::BOOL, true},
- {"feeRate", RPCArg::Type::AMOUNT, true},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR,
+ {"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"
+ " 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.",
{
- {"int", RPCArg::Type::NUM, true},
+ {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
},
- true},
- {"replaceable", RPCArg::Type::BOOL, true},
- {"conf_target", RPCArg::Type::NUM, true},
- {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ {"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\""},
},
- true, "options"},
- {"bip32derivs", RPCArg::Type::BOOL, true},
+ "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() +
- "\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"
- " 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"
- " \"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"
"\nResult:\n"
"{\n"
" \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 623c5c39a2..1ed1926af2 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -34,12 +34,12 @@ static void AddKey(CWallet& wallet, const CKey& key)
wallet.AddKeyPubKey(key, key.GetPubKey());
}
-BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* const nullBlock = nullptr;
+ const CBlockIndex* const null_block = nullptr;
CBlockIndex* oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
@@ -47,6 +47,19 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
auto locked_chain = chain->lock();
+ // Verify ScanForWalletTransactions accomodates a null start block.
+ {
+ CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ AddKey(wallet, coinbaseKey);
+ WalletRescanReserver reserver(&wallet);
+ reserver.reserve();
+ const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
+ BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(nullptr, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
+ BOOST_CHECK_EQUAL(failed_block, null_block);
+ BOOST_CHECK_EQUAL(stop_block, null_block);
+ BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ }
+
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
@@ -54,7 +67,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
+ const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
+ BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
+ BOOST_CHECK_EQUAL(failed_block, null_block);
+ BOOST_CHECK_EQUAL(stop_block, newTip);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
@@ -69,10 +85,47 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
+ const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
+ BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
+ BOOST_CHECK_EQUAL(failed_block, oldTip);
+ BOOST_CHECK_EQUAL(stop_block, newTip);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
+ // Prune the remaining block file.
+ PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
+
+ // Verify ScanForWalletTransactions scans no blocks.
+ {
+ CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ AddKey(wallet, coinbaseKey);
+ WalletRescanReserver reserver(&wallet);
+ reserver.reserve();
+ const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
+ BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
+ BOOST_CHECK_EQUAL(failed_block, newTip);
+ BOOST_CHECK_EQUAL(stop_block, null_block);
+ BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
+{
+ auto chain = interfaces::MakeChain();
+
+ // Cap last block file size, and mine new block in a new block file.
+ CBlockIndex* oldTip = chainActive.Tip();
+ GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
+ CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+ CBlockIndex* newTip = chainActive.Tip();
+
+ auto locked_chain = chain->lock();
+
+ // Prune the older block file.
+ PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
+
// Verify importmulti RPC returns failure for a key whose creation time is
// before the missing block, and success for a key whose creation time is
// after.
@@ -287,7 +340,11 @@ public:
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
- wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver);
+ const CBlockIndex* const null_block = nullptr;
+ const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
+ BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
+ BOOST_CHECK_EQUAL(stop_block, chainActive.Tip());
+ BOOST_CHECK_EQUAL(failed_block, null_block);
}
~ListCoinsTestingSetup()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d7798e005f..109f8e6da0 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -22,6 +22,7 @@
#include <policy/rbf.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <shutdown.h>
#include <timedata.h>
@@ -104,67 +105,17 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
-/** A class to identify which pubkeys a script and a keystore have in common. */
-class CAffectedKeysVisitor : public boost::static_visitor<void> {
-private:
- const CKeyStore &keystore;
- std::vector<CKeyID> &vKeys;
-
-public:
- /**
- * @param[in] keystoreIn The CKeyStore that is queried for the presence of a pubkey.
- * @param[out] vKeysIn A vector to which a script's pubkey identifiers are appended if they are in the keystore.
- */
- CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
-
- /**
- * Apply the visitor to each destination in a script, recursively to the redeemscript
- * in the case of p2sh destinations.
- * @param[in] script The CScript from which destinations are extracted.
- * @post Any CKeyIDs that script and keystore have in common are appended to the visitor's vKeys.
- */
- void Process(const CScript &script) {
- txnouttype type;
- std::vector<CTxDestination> vDest;
- int nRequired;
- if (ExtractDestinations(script, type, vDest, nRequired)) {
- for (const CTxDestination &dest : vDest)
- boost::apply_visitor(*this, dest);
- }
- }
-
- void operator()(const CKeyID &keyId) {
- if (keystore.HaveKey(keyId))
- vKeys.push_back(keyId);
- }
-
- void operator()(const CScriptID &scriptId) {
- CScript script;
- if (keystore.GetCScript(scriptId, script))
- Process(script);
- }
-
- void operator()(const WitnessV0ScriptHash& scriptID)
- {
- CScriptID id;
- CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin());
- CScript script;
- if (keystore.GetCScript(id, script)) {
- Process(script);
- }
- }
-
- void operator()(const WitnessV0KeyHash& keyid)
- {
- CKeyID id(keyid);
- if (keystore.HaveKey(id)) {
- vKeys.push_back(id);
- }
+std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider)
+{
+ std::vector<CScript> dummy;
+ FlatSigningProvider out;
+ InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out);
+ std::vector<CKeyID> ret;
+ for (const auto& entry : out.pubkeys) {
+ ret.push_back(entry.first);
}
-
- template<typename X>
- void operator()(const X &none) {}
-};
+ return ret;
+}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
@@ -977,9 +928,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
// loop though all outputs
for (const CTxOut& txout: tx.vout) {
// extract addresses and check if they match with an unused keypool key
- std::vector<CKeyID> vAffected;
- CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
- for (const CKeyID &keyid : vAffected) {
+ for (const auto& keyid : GetAffectedKeys(txout.scriptPubKey, *this)) {
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
@@ -1611,8 +1560,9 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
}
if (startBlock) {
- const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, reserver, update);
- if (failedBlock) {
+ const CBlockIndex *failedBlock, *stop_block;
+ // TODO: this should take into account failure by ScanResult::USER_ABORT
+ if (ScanResult::FAILURE == ScanForWalletTransactions(startBlock, nullptr, reserver, failedBlock, stop_block, update)) {
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
}
}
@@ -1624,18 +1574,22 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
- * Returns null if scan was successful. Otherwise, if a complete rescan was not
- * possible (due to pruning or corruption), returns pointer to the most recent
- * block that could not be scanned.
+ * @param[in] pindexStop if not a nullptr, the scan will stop at this block-index
+ * @param[out] failed_block if FAILURE is returned, the most recent block
+ * that could not be scanned, otherwise nullptr
+ * @param[out] stop_block the most recent block that could be scanned,
+ * otherwise nullptr if no block could be scanned
*
- * If pindexStop is not a nullptr, the scan will stop at the block-index
- * defined by pindexStop
+ * @return ScanResult indicating success or failure of the scan. SUCCESS if
+ * scan was successful. FAILURE if a complete rescan was not possible (due to
+ * pruning or corruption). USER_ABORT if the rescan was aborted before it
+ * could complete.
*
- * Caller needs to make sure pindexStop (and the optional pindexStart) are on
+ * @pre Caller needs to make sure pindexStop (and the optional pindexStart) are on
* the main chain after to the addition of any new keys you want to detect
* transactions for.
*/
-CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver &reserver, bool fUpdate)
+CWallet::ScanResult CWallet::ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate)
{
int64_t nNow = GetTime();
const CChainParams& chainParams = Params();
@@ -1645,8 +1599,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
assert(pindexStop->nHeight >= pindexStart->nHeight);
}
- CBlockIndex* pindex = pindexStart;
- CBlockIndex* ret = nullptr;
+ const CBlockIndex* pindex = pindexStart;
+ failed_block = nullptr;
+ stop_block = nullptr;
if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight);
@@ -1667,8 +1622,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
}
}
double progress_current = progress_begin;
- while (pindex && !fAbortRescan && !ShutdownRequested())
- {
+ while (pindex && !fAbortRescan && !ShutdownRequested()) {
if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) {
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
}
@@ -1684,14 +1638,17 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
if (pindex && !chainActive.Contains(pindex)) {
// Abort scan if current block is no longer active, to prevent
// marking transactions as coming from the wrong block.
- ret = pindex;
+ failed_block = pindex;
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
+ // scan succeeded, record block as most recent successfully scanned
+ stop_block = pindex;
} else {
- ret = pindex;
+ // could not scan block, keep scanning but record this block as the most recent failure
+ failed_block = pindex;
}
if (pindex == pindexStop) {
break;
@@ -1707,14 +1664,20 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
}
}
}
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
if (pindex && fAbortRescan) {
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current);
+ return ScanResult::USER_ABORT;
} else if (pindex && ShutdownRequested()) {
WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current);
+ return ScanResult::USER_ABORT;
}
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
}
- return ret;
+ if (failed_block) {
+ return ScanResult::FAILURE;
+ } else {
+ return ScanResult::SUCCESS;
+ }
}
void CWallet::ReacceptWalletTransactions()
@@ -2449,7 +2412,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Cases where we have 11+ outputs all pointing to the same destination may result in
// privacy leaks as they will potentially be deterministically sorted. We solve that by
// explicitly shuffling the outputs before processing
- std::shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
+ Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
}
std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
@@ -2909,7 +2872,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// Shuffle selected coins and fill in final vin
txNew.vin.clear();
std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
- std::shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
+ Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
// Note how the sequence number is set to non-maxint so that
// the nLockTime set above actually works.
@@ -3698,7 +3661,6 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
return;
// find first block that affects those keys, if there are any left
- std::vector<CKeyID> vAffected;
for (const auto& entry : mapWallet) {
// iterate over all wallet transactions...
const CWalletTx &wtx = entry.second;
@@ -3708,14 +3670,12 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
int nHeight = pindex->nHeight;
for (const CTxOut &txout : wtx.tx->vout) {
// iterate over all their outputs
- CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
- for (const CKeyID &keyid : vAffected) {
+ for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *this)) {
// ... and all their affected keys
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
rit->second = pindex;
}
- vAffected.clear();
}
}
}
@@ -4169,11 +4129,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
nStart = GetTimeMillis();
{
WalletRescanReserver reserver(walletInstance.get());
- if (!reserver.reserve()) {
+ const CBlockIndex *stop_block, *failed_block;
+ if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, failed_block, stop_block, true))) {
InitError(_("Failed to rescan the wallet during initialization"));
return nullptr;
}
- walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
}
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
walletInstance->ChainStateFlushed(chainActive.GetLocator());
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4291163bea..95a2c833f8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -899,7 +899,13 @@ public:
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
- CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
+
+ enum class ScanResult {
+ SUCCESS,
+ FAILURE,
+ USER_ABORT
+ };
+ ScanResult ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);