aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/addrman.cpp36
-rw-r--r--src/addrman.h5
-rw-r--r--src/amount.h2
-rw-r--r--src/arith_uint256.h3
-rw-r--r--src/bench/block_assemble.cpp4
-rw-r--r--src/bench/ccoins_caching.cpp9
-rw-r--r--src/bench/coin_selection.cpp29
-rw-r--r--src/bench/duplicate_inputs.cpp100
-rw-r--r--src/bench/gcs_filter.cpp4
-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/blockfilter.cpp62
-rw-r--r--src/blockfilter.h54
-rw-r--r--src/chain.h9
-rw-r--r--src/compat.h9
-rw-r--r--src/consensus/tx_verify.cpp2
-rw-r--r--src/fs.h2
-rw-r--r--src/hash.h10
-rw-r--r--src/httpserver.cpp32
-rw-r--r--src/init.cpp27
-rw-r--r--src/key_io.cpp2
-rw-r--r--src/miner.cpp4
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp210
-rw-r--r--src/net.h58
-rw-r--r--src/net_processing.cpp7
-rw-r--r--src/netbase.cpp25
-rw-r--r--src/noui.h2
-rw-r--r--src/policy/fees.cpp17
-rw-r--r--src/policy/fees.h40
-rw-r--r--src/primitives/transaction.h8
-rw-r--r--src/qt/bitcoin.cpp23
-rw-r--r--src/qt/bitcoingui.cpp96
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/clientmodel.cpp14
-rw-r--r--src/qt/coincontroldialog.cpp20
-rw-r--r--src/qt/coincontroldialog.h9
-rw-r--r--src/qt/forms/coincontroldialog.ui2
-rw-r--r--src/qt/forms/optionsdialog.ui2
-rw-r--r--src/qt/guiconstants.h1
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/macnotificationhandler.mm23
-rw-r--r--src/qt/networkstyle.cpp2
-rw-r--r--src/qt/notificator.cpp20
-rw-r--r--src/qt/notificator.h6
-rw-r--r--src/qt/overviewpage.cpp32
-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.cpp11
-rw-r--r--src/qt/splashscreen.h7
-rw-r--r--src/qt/test/wallettests.cpp8
-rw-r--r--src/qt/transactiontablemodel.cpp5
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletmodel.cpp12
-rw-r--r--src/random.cpp15
-rw-r--r--src/random.h31
-rw-r--r--src/rest.cpp17
-rw-r--r--src/rpc/blockchain.cpp224
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp122
-rw-r--r--src/rpc/misc.cpp83
-rw-r--r--src/rpc/net.cpp42
-rw-r--r--src/rpc/rawtransaction.cpp346
-rw-r--r--src/rpc/server.cpp34
-rw-r--r--src/rpc/util.cpp235
-rw-r--r--src/rpc/util.h66
-rw-r--r--src/scheduler.cpp5
-rw-r--r--src/scheduler.h2
-rw-r--r--src/script/descriptor.cpp504
-rw-r--r--src/script/descriptor.h33
-rw-r--r--src/script/sign.cpp2
-rw-r--r--src/script/sign.h5
-rw-r--r--src/streams.h10
-rw-r--r--src/test/addrman_tests.cpp72
-rw-r--r--src/test/blockchain_tests.cpp7
-rw-r--r--src/test/blockfilter_tests.cpp26
-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.cpp50
-rw-r--r--src/test/key_io_tests.cpp4
-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/scheduler_tests.cpp17
-rw-r--r--src/test/script_p2sh_tests.cpp76
-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.cpp31
-rw-r--r--src/test/test_bitcoin.h39
-rw-r--r--src/test/transaction_tests.cpp92
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp34
-rw-r--r--src/test/validation_block_tests.cpp19
-rw-r--r--src/threadinterrupt.h2
-rw-r--r--src/torcontrol.cpp23
-rw-r--r--src/uint256.h11
-rw-r--r--src/validation.cpp84
-rw-r--r--src/validation.h6
-rw-r--r--src/validationinterface.cpp61
-rw-r--r--src/wallet/coinselection.cpp2
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/rpcdump.cpp507
-rw-r--r--src/wallet/rpcwallet.cpp821
-rw-r--r--src/wallet/test/wallet_tests.cpp111
-rw-r--r--src/wallet/wallet.cpp147
-rw-r--r--src/wallet/wallet.h11
123 files changed, 3069 insertions, 2507 deletions
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 544092520f..5e787ca222 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -18,6 +18,7 @@ bench_bench_bitcoin_SOURCES = \
bench/block_assemble.cpp \
bench/checkblock.cpp \
bench/checkqueue.cpp \
+ bench/duplicate_inputs.cpp \
bench/examples.cpp \
bench/rollingbloom.cpp \
bench/crypto_hash.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a31852c94f..8ce7562434 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -132,7 +132,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 093b263ab3..06c342ba73 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -11,22 +11,22 @@
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash();
- uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
+ uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
}
int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const
{
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetCheapHash();
- uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetCheapHash();
+ uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
}
int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetHash().GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
@@ -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/amount.h b/src/amount.h
index 449fd1b15f..47968e80b1 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2016 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
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/block_assemble.cpp b/src/bench/block_assemble.cpp
index 2def0b23e2..8133fd9d65 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -27,7 +27,7 @@ static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey
{
auto block = std::make_shared<CBlock>(
BlockAssembler{Params()}
- .CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true)
+ .CreateNewBlock(coinbase_scriptPubKey)
->block);
block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
@@ -78,7 +78,7 @@ static void AssembleBlock(benchmark::State& state)
::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
const CChainParams& chainparams = Params();
- thread_group.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler));
+ thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
LoadGenesisBlock(chainparams);
CValidationState state;
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/coin_selection.cpp b/src/bench/coin_selection.cpp
index 8552ed34fd..74641191a1 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -4,25 +4,19 @@
#include <bench/bench.h>
#include <interfaces/chain.h>
-#include <wallet/wallet.h>
#include <wallet/coinselection.h>
+#include <wallet/wallet.h>
#include <set>
-static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<OutputGroup>& groups)
+static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs)
{
- int nInput = 0;
-
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- tx.vout.resize(nInput + 1);
- tx.vout[nInput].nValue = nValue;
- CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
-
- int nAge = 6 * 24;
- COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
+ tx.vout.resize(1);
+ tx.vout[0].nValue = nValue;
+ wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -36,14 +30,21 @@ static void CoinSelection(benchmark::State& state)
{
auto chain = interfaces::MakeChain();
const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
// Add coins.
- std::vector<OutputGroup> groups;
for (int i = 0; i < 1000; ++i) {
- addCoin(1000 * COIN, wallet, groups);
+ addCoin(1000 * COIN, wallet, wtxs);
+ }
+ addCoin(3 * COIN, wallet, wtxs);
+
+ // Create groups
+ std::vector<OutputGroup> groups;
+ for (const auto& wtx : wtxs) {
+ COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
+ groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
}
- addCoin(3 * COIN, wallet, groups);
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
new file mode 100644
index 0000000000..e0854e2c62
--- /dev/null
+++ b/src/bench/duplicate_inputs.cpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <miner.h>
+#include <policy/policy.h>
+#include <pow.h>
+#include <scheduler.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <util/time.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+#include <boost/thread.hpp>
+
+#include <list>
+#include <vector>
+
+
+static void DuplicateInputs(benchmark::State& state)
+{
+ const CScript SCRIPT_PUB{CScript(OP_TRUE)};
+
+ // Switch to regtest so we can mine faster
+ // Also segwit is active, so we can include witness transactions
+ SelectParams(CBaseChainParams::REGTEST);
+
+ InitScriptExecutionCache();
+
+ boost::thread_group thread_group;
+ CScheduler scheduler;
+ const CChainParams& chainparams = Params();
+ {
+ ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+
+ thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ LoadGenesisBlock(chainparams);
+ CValidationState cvstate;
+ ActivateBestChain(cvstate, chainparams);
+ assert(::chainActive.Tip() != nullptr);
+ const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
+ assert(witness_enabled);
+ }
+
+ CBlock block{};
+ CMutableTransaction coinbaseTx{};
+ CMutableTransaction naughtyTx{};
+
+ CBlockIndex* pindexPrev = ::chainActive.Tip();
+ assert(pindexPrev != nullptr);
+ block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
+ block.nNonce = 0;
+ auto nHeight = pindexPrev->nHeight + 1;
+
+ // Make a coinbase TX
+ coinbaseTx.vin.resize(1);
+ coinbaseTx.vin[0].prevout.SetNull();
+ coinbaseTx.vout.resize(1);
+ coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB;
+ coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
+ coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
+
+
+ naughtyTx.vout.resize(1);
+ naughtyTx.vout[0].nValue = 0;
+ naughtyTx.vout[0].scriptPubKey = SCRIPT_PUB;
+
+ uint64_t n_inputs = (((MAX_BLOCK_SERIALIZED_SIZE / WITNESS_SCALE_FACTOR) - (CTransaction(coinbaseTx).GetTotalSize() + CTransaction(naughtyTx).GetTotalSize())) / 41) - 100;
+ for (uint64_t x = 0; x < (n_inputs - 1); ++x) {
+ naughtyTx.vin.emplace_back(GetRandHash(), 0, CScript(), 0);
+ }
+ naughtyTx.vin.emplace_back(naughtyTx.vin.back());
+
+ block.vtx.push_back(MakeTransactionRef(std::move(coinbaseTx)));
+ block.vtx.push_back(MakeTransactionRef(std::move(naughtyTx)));
+
+ block.hashMerkleRoot = BlockMerkleRoot(block);
+
+ while (state.KeepRunning()) {
+ CValidationState cvstate{};
+ assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
+ assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
+ }
+
+ thread_group.interrupt_all();
+ thread_group.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+}
+
+BENCHMARK(DuplicateInputs, 10);
diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp
index 6f4e384e3b..535ad35571 100644
--- a/src/bench/gcs_filter.cpp
+++ b/src/bench/gcs_filter.cpp
@@ -17,7 +17,7 @@ static void ConstructGCSFilter(benchmark::State& state)
uint64_t siphash_k0 = 0;
while (state.KeepRunning()) {
- GCSFilter filter(siphash_k0, 0, 20, 1 << 20, elements);
+ GCSFilter filter({siphash_k0, 0, 20, 1 << 20}, elements);
siphash_k0++;
}
@@ -32,7 +32,7 @@ static void MatchGCSFilter(benchmark::State& state)
element[1] = static_cast<unsigned char>(i >> 8);
elements.insert(std::move(element));
}
- GCSFilter filter(0, 0, 20, 1 << 20, elements);
+ GCSFilter filter({0, 0, 20, 1 << 20}, elements);
while (state.KeepRunning()) {
filter.Match(GCSFilter::Element());
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/blockfilter.cpp b/src/blockfilter.cpp
index 163e2a52ef..bcf24047ff 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -79,7 +79,7 @@ static uint64_t MapIntoRange(uint64_t x, uint64_t n)
uint64_t GCSFilter::HashToRange(const Element& element) const
{
- uint64_t hash = CSipHasher(m_siphash_k0, m_siphash_k1)
+ uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
.Write(element.data(), element.size())
.Finalize();
return MapIntoRange(hash, m_F);
@@ -96,16 +96,13 @@ std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) cons
return hashed_elements;
}
-GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M)
- : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M), m_N(0), m_F(0)
+GCSFilter::GCSFilter(const Params& params)
+ : m_params(params), m_N(0), m_F(0), m_encoded{0}
{}
-GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
- std::vector<unsigned char> encoded_filter)
- : GCSFilter(siphash_k0, siphash_k1, P, M)
+GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter)
+ : m_params(params), m_encoded(std::move(encoded_filter))
{
- m_encoded = std::move(encoded_filter);
-
VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
uint64_t N = ReadCompactSize(stream);
@@ -113,29 +110,28 @@ GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32
if (m_N != N) {
throw std::ios_base::failure("N must be <2^32");
}
- m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M);
+ m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
// Verify that the encoded filter contains exactly N elements. If it has too much or too little
// data, a std::ios_base::failure exception will be raised.
BitStreamReader<VectorReader> bitreader(stream);
for (uint64_t i = 0; i < m_N; ++i) {
- GolombRiceDecode(bitreader, m_P);
+ GolombRiceDecode(bitreader, m_params.m_P);
}
if (!stream.empty()) {
throw std::ios_base::failure("encoded_filter contains excess data");
}
}
-GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
- const ElementSet& elements)
- : GCSFilter(siphash_k0, siphash_k1, P, M)
+GCSFilter::GCSFilter(const Params& params, const ElementSet& elements)
+ : m_params(params)
{
size_t N = elements.size();
m_N = static_cast<uint32_t>(N);
if (m_N != N) {
throw std::invalid_argument("N must be <2^32");
}
- m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M);
+ m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
@@ -150,7 +146,7 @@ GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32
uint64_t last_value = 0;
for (uint64_t value : BuildHashedSet(elements)) {
uint64_t delta = value - last_value;
- GolombRiceEncode(bitwriter, m_P, delta);
+ GolombRiceEncode(bitwriter, m_params.m_P, delta);
last_value = value;
}
@@ -170,7 +166,7 @@ bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const
uint64_t value = 0;
size_t hashes_index = 0;
for (uint32_t i = 0; i < m_N; ++i) {
- uint64_t delta = GolombRiceDecode(bitreader, m_P);
+ uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P);
value += delta;
while (true) {
@@ -225,19 +221,39 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
return elements;
}
+BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
+ std::vector<unsigned char> filter)
+ : m_filter_type(filter_type), m_block_hash(block_hash)
+{
+ GCSFilter::Params params;
+ if (!BuildParams(params)) {
+ throw std::invalid_argument("unknown filter_type");
+ }
+ m_filter = GCSFilter(params, std::move(filter));
+}
+
BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
: m_filter_type(filter_type), m_block_hash(block.GetHash())
{
+ GCSFilter::Params params;
+ if (!BuildParams(params)) {
+ throw std::invalid_argument("unknown filter_type");
+ }
+ m_filter = GCSFilter(params, BasicFilterElements(block, block_undo));
+}
+
+bool BlockFilter::BuildParams(GCSFilter::Params& params) const
+{
switch (m_filter_type) {
case BlockFilterType::BASIC:
- m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1),
- BASIC_FILTER_P, BASIC_FILTER_M,
- BasicFilterElements(block, block_undo));
- break;
-
- default:
- throw std::invalid_argument("unknown filter_type");
+ params.m_siphash_k0 = m_block_hash.GetUint64(0);
+ params.m_siphash_k1 = m_block_hash.GetUint64(1);
+ params.m_P = BASIC_FILTER_P;
+ params.m_M = BASIC_FILTER_M;
+ return true;
}
+
+ return false;
}
uint256 BlockFilter::GetHash() const
diff --git a/src/blockfilter.h b/src/blockfilter.h
index 871be11769..4d1f51dd60 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -25,11 +25,20 @@ public:
typedef std::vector<unsigned char> Element;
typedef std::unordered_set<Element, ByteVectorHash> ElementSet;
+ struct Params
+ {
+ uint64_t m_siphash_k0;
+ uint64_t m_siphash_k1;
+ uint8_t m_P; //!< Golomb-Rice coding parameter
+ uint32_t m_M; //!< Inverse false positive rate
+
+ Params(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 1)
+ : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M)
+ {}
+ };
+
private:
- uint64_t m_siphash_k0;
- uint64_t m_siphash_k1;
- uint8_t m_P; //!< Golomb-Rice coding parameter
- uint32_t m_M; //!< Inverse false positive rate
+ Params m_params;
uint32_t m_N; //!< Number of elements in the filter
uint64_t m_F; //!< Range of element hashes, F = N * M
std::vector<unsigned char> m_encoded;
@@ -45,19 +54,16 @@ private:
public:
/** Constructs an empty filter. */
- GCSFilter(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 0);
+ explicit GCSFilter(const Params& params = Params());
/** Reconstructs an already-created filter from an encoding. */
- GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
- std::vector<unsigned char> encoded_filter);
+ GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter);
/** Builds a new filter from the params and set of elements. */
- GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
- const ElementSet& elements);
+ GCSFilter(const Params& params, const ElementSet& elements);
- uint8_t GetP() const { return m_P; }
uint32_t GetN() const { return m_N; }
- uint32_t GetM() const { return m_M; }
+ const Params& GetParams() const { return m_params; }
const std::vector<unsigned char>& GetEncoded() const { return m_encoded; }
/**
@@ -93,13 +99,21 @@ private:
uint256 m_block_hash;
GCSFilter m_filter;
+ bool BuildParams(GCSFilter::Params& params) const;
+
public:
- // Construct a new BlockFilter of the specified type from a block.
+ BlockFilter() = default;
+
+ //! Reconstruct a BlockFilter from parts.
+ BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
+ std::vector<unsigned char> filter);
+
+ //! Construct a new BlockFilter of the specified type from a block.
BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo);
BlockFilterType GetFilterType() const { return m_filter_type; }
-
+ const uint256& GetBlockHash() const { return m_block_hash; }
const GCSFilter& GetFilter() const { return m_filter; }
const std::vector<unsigned char>& GetEncodedFilter() const
@@ -107,10 +121,10 @@ public:
return m_filter.GetEncoded();
}
- // Compute the filter hash.
+ //! Compute the filter hash.
uint256 GetHash() const;
- // Compute the filter header given the previous one.
+ //! Compute the filter header given the previous one.
uint256 ComputeHeader(const uint256& prev_header) const;
template <typename Stream>
@@ -131,15 +145,11 @@ public:
m_filter_type = static_cast<BlockFilterType>(filter_type);
- switch (m_filter_type) {
- case BlockFilterType::BASIC:
- m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1),
- BASIC_FILTER_P, BASIC_FILTER_M, std::move(encoded_filter));
- break;
-
- default:
+ GCSFilter::Params params;
+ if (!BuildParams(params)) {
throw std::ios_base::failure("unknown filter_type");
}
+ m_filter = GCSFilter(params, std::move(encoded_filter));
}
};
diff --git a/src/chain.h b/src/chain.h
index 1c0c5a7e69..3b786664af 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -294,6 +294,15 @@ public:
return *phashBlock;
}
+ /**
+ * Check whether this block's and all previous blocks' transactions have been
+ * downloaded (and stored to disk) at some point.
+ *
+ * Does not imply the transactions are consensus-valid (ConnectTip might fail)
+ * Does not imply the transactions are still stored on disk. (IsBlockPruned might return true)
+ */
+ bool HaveTxsDownloaded() const { return nChainTx != 0; }
+
int64_t GetBlockTime() const
{
return (int64_t)nTime;
diff --git a/src/compat.h b/src/compat.h
index 049579c365..7b164d5630 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -92,8 +92,15 @@ typedef void* sockopt_arg_type;
typedef char* sockopt_arg_type;
#endif
+// Note these both should work with the current usage of poll, but best to be safe
+// WIN32 poll is broken https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
+// __APPLE__ poll is broke https://github.com/bitcoin/bitcoin/pull/14336#issuecomment-437384408
+#if defined(__linux__)
+#define USE_POLL
+#endif
+
bool static inline IsSelectableSocket(const SOCKET& s) {
-#ifdef WIN32
+#if defined(USE_POLL) || defined(WIN32)
return true;
#else
return (s < FD_SETSIZE);
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index b17a8bb31d..0a7eacfb91 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2017 The Bitcoin Core developers
+// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/fs.h b/src/fs.h
index bdccb15232..8af81f173b 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Bitcoin Core developers
+// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/hash.h b/src/hash.h
index 6acab0b161..c295568a3e 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
+#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
#include <prevector.h>
@@ -138,6 +139,15 @@ public:
return result;
}
+ /**
+ * Returns the first 64 bits from the resulting hash.
+ */
+ inline uint64_t GetCheapHash() {
+ unsigned char result[CHash256::OUTPUT_SIZE];
+ ctx.Finalize(result);
+ return ReadLE64(result);
+ }
+
template<typename T>
CHashWriter& operator<<(const T& obj) {
// Serialize to this stream
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 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..18c145a023 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -59,7 +59,6 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
-#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
@@ -403,6 +402,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 +499,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 +856,7 @@ int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
+int64_t peer_connect_timeout;
} // namespace
@@ -952,8 +953,13 @@ bool AppInitParameterInteraction()
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
- nMaxConnections = std::max(std::min<int>(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
+#ifdef USE_POLL
+ int fd_max = nFD;
+#else
+ int fd_max = FD_SETSIZE;
+#endif
+ nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
@@ -1054,8 +1060,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;
@@ -1229,8 +1241,8 @@ bool AppInitMain(InitInterfaces& interfaces)
}
// Start the lightweight task scheduler thread
- CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
- threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
+ CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
+ threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
GetMainSignals().RegisterWithMempoolSignals(mempool);
@@ -1638,7 +1650,7 @@ bool AppInitMain(InitInterfaces& interfaces)
vImportFiles.push_back(strFile);
}
- threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));
+ threadGroup.create_thread(std::bind(&ThreadImport, vImportFiles));
// Wait for genesis block to be processed
{
@@ -1693,6 +1705,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/key_io.cpp b/src/key_io.cpp
index 282385f50d..d998089535 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017 The Bitcoin Core developers
+// Copyright (c) 2014-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/miner.cpp b/src/miner.cpp
index 96c9cd6d2a..ef48a86e32 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -95,7 +95,7 @@ void BlockAssembler::resetBlock()
nFees = 0;
}
-std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx)
+std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
int64_t nTimeStart = GetTimeMicros();
@@ -139,7 +139,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// not activated.
// TODO: replace this with a call to main to assess validity of a mempool
// transaction (which in most cases can be a no-op).
- fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx;
+ fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
int nPackagesSelected = 0;
int nDescendantsUpdated = 0;
diff --git a/src/miner.h b/src/miner.h
index 8cdcf7133b..44c50b01ad 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -157,7 +157,7 @@ public:
BlockAssembler(const CChainParams& params, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
- std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx=true);
+ std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
private:
// utility functions
diff --git a/src/net.cpp b/src/net.cpp
index 65a308780a..a288c6e81e 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -26,6 +26,10 @@
#include <fcntl.h>
#endif
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+
#ifdef USE_UPNP
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/miniwget.h>
@@ -33,6 +37,7 @@
#include <miniupnpc/upnperrors.h>
#endif
+#include <unordered_map>
#include <math.h>
@@ -71,6 +76,10 @@ enum BindFlags {
BF_WHITELIST = (1U << 2),
};
+// The set of sockets cannot be modified while waiting
+// The sleep time needs to be small to avoid new sockets stalling
+static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50;
+
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
@@ -82,8 +91,8 @@ bool fDiscover = true;
bool fListen = true;
bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
-std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
-static bool vfLimited[NET_MAX] = {};
+std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
+static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
std::string strSubVersion;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
@@ -134,11 +143,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 +199,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);
}
}
}
@@ -715,7 +725,10 @@ void CNode::copyStats(CNodeStats &stats)
X(nRecvBytes);
}
X(fWhitelisted);
- X(minFeeFilter);
+ {
+ LOCK(cs_feeFilter);
+ X(minFeeFilter);
+ }
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
@@ -874,16 +887,7 @@ const uint256& CNetMessage::GetMessageHash() const
return data_hash;
}
-
-
-
-
-
-
-
-
-// requires LOCK(cs_vSend)
-size_t CConnman::SocketSendData(CNode *pnode) const
+size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend)
{
auto it = pnode->vSendMsg.begin();
size_t nSentSize = 0;
@@ -1011,6 +1015,7 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
+ LOCK(node->cs_filter);
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
@@ -1232,11 +1237,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)
@@ -1262,28 +1267,10 @@ void CConnman::InactivityCheck(CNode *pnode)
}
}
-void CConnman::SocketHandler()
+bool CConnman::GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
{
- //
- // Find which sockets have data to receive
- //
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = 50000; // frequency to poll pnode->vSend
-
- fd_set fdsetRecv;
- fd_set fdsetSend;
- fd_set fdsetError;
- FD_ZERO(&fdsetRecv);
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- SOCKET hSocketMax = 0;
- bool have_fds = false;
-
for (const ListenSocket& hListenSocket : vhListenSocket) {
- FD_SET(hListenSocket.socket, &fdsetRecv);
- hSocketMax = std::max(hSocketMax, hListenSocket.socket);
- have_fds = true;
+ recv_set.insert(hListenSocket.socket);
}
{
@@ -1312,46 +1299,151 @@ void CConnman::SocketHandler()
if (pnode->hSocket == INVALID_SOCKET)
continue;
- FD_SET(pnode->hSocket, &fdsetError);
- hSocketMax = std::max(hSocketMax, pnode->hSocket);
- have_fds = true;
-
+ error_set.insert(pnode->hSocket);
if (select_send) {
- FD_SET(pnode->hSocket, &fdsetSend);
+ send_set.insert(pnode->hSocket);
continue;
}
if (select_recv) {
- FD_SET(pnode->hSocket, &fdsetRecv);
+ recv_set.insert(pnode->hSocket);
}
}
}
- int nSelect = select(have_fds ? hSocketMax + 1 : 0,
- &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
+ return !recv_set.empty() || !send_set.empty() || !error_set.empty();
+}
+
+#ifdef USE_POLL
+void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
+{
+ std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
+ if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) {
+ interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
+ return;
+ }
+
+ std::unordered_map<SOCKET, struct pollfd> pollfds;
+ for (SOCKET socket_id : recv_select_set) {
+ pollfds[socket_id].fd = socket_id;
+ pollfds[socket_id].events |= POLLIN;
+ }
+
+ for (SOCKET socket_id : send_select_set) {
+ pollfds[socket_id].fd = socket_id;
+ pollfds[socket_id].events |= POLLOUT;
+ }
+
+ for (SOCKET socket_id : error_select_set) {
+ pollfds[socket_id].fd = socket_id;
+ // These flags are ignored, but we set them for clarity
+ pollfds[socket_id].events |= POLLERR|POLLHUP;
+ }
+
+ std::vector<struct pollfd> vpollfds;
+ vpollfds.reserve(pollfds.size());
+ for (auto it : pollfds) {
+ vpollfds.push_back(std::move(it.second));
+ }
+
+ if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) return;
+
+ if (interruptNet) return;
+
+ for (struct pollfd pollfd_entry : vpollfds) {
+ if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd);
+ if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd);
+ if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd);
+ }
+}
+#else
+void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
+{
+ std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
+ if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) {
+ interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
+ return;
+ }
+
+ //
+ // Find which sockets have data to receive
+ //
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend
+
+ fd_set fdsetRecv;
+ fd_set fdsetSend;
+ fd_set fdsetError;
+ FD_ZERO(&fdsetRecv);
+ FD_ZERO(&fdsetSend);
+ FD_ZERO(&fdsetError);
+ SOCKET hSocketMax = 0;
+
+ for (SOCKET hSocket : recv_select_set) {
+ FD_SET(hSocket, &fdsetRecv);
+ hSocketMax = std::max(hSocketMax, hSocket);
+ }
+
+ for (SOCKET hSocket : send_select_set) {
+ FD_SET(hSocket, &fdsetSend);
+ hSocketMax = std::max(hSocketMax, hSocket);
+ }
+
+ for (SOCKET hSocket : error_select_set) {
+ FD_SET(hSocket, &fdsetError);
+ hSocketMax = std::max(hSocketMax, hSocket);
+ }
+
+ int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
+
if (interruptNet)
return;
if (nSelect == SOCKET_ERROR)
{
- if (have_fds)
- {
- int nErr = WSAGetLastError();
- LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
- for (unsigned int i = 0; i <= hSocketMax; i++)
- FD_SET(i, &fdsetRecv);
- }
+ int nErr = WSAGetLastError();
+ LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
+ for (unsigned int i = 0; i <= hSocketMax; i++)
+ FD_SET(i, &fdsetRecv);
FD_ZERO(&fdsetSend);
FD_ZERO(&fdsetError);
- if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
+ if (!interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)))
return;
}
+ for (SOCKET hSocket : recv_select_set) {
+ if (FD_ISSET(hSocket, &fdsetRecv)) {
+ recv_set.insert(hSocket);
+ }
+ }
+
+ for (SOCKET hSocket : send_select_set) {
+ if (FD_ISSET(hSocket, &fdsetSend)) {
+ send_set.insert(hSocket);
+ }
+ }
+
+ for (SOCKET hSocket : error_select_set) {
+ if (FD_ISSET(hSocket, &fdsetError)) {
+ error_set.insert(hSocket);
+ }
+ }
+}
+#endif
+
+void CConnman::SocketHandler()
+{
+ std::set<SOCKET> recv_set, send_set, error_set;
+ SocketEvents(recv_set, send_set, error_set);
+
+ if (interruptNet) return;
+
//
// Accept new connections
//
for (const ListenSocket& hListenSocket : vhListenSocket)
{
- if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
+ if (hListenSocket.socket != INVALID_SOCKET && recv_set.count(hListenSocket.socket) > 0)
{
AcceptConnection(hListenSocket);
}
@@ -1382,9 +1474,9 @@ void CConnman::SocketHandler()
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
- recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
- sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
- errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
+ recvSet = recv_set.count(pnode->hSocket) > 0;
+ sendSet = send_set.count(pnode->hSocket) > 0;
+ errorSet = error_set.count(pnode->hSocket) > 0;
}
if (recvSet || errorSet)
{
diff --git a/src/net.h b/src/net.h
index 164ec9080c..915a8e5b35 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;
@@ -342,6 +346,8 @@ private:
void DisconnectNodes();
void NotifyNumConnectionsChanged();
void InactivityCheck(CNode *pnode);
+ bool GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set);
+ void SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set);
void SocketHandler();
void ThreadSocketHandler();
void ThreadDNSAddressSeed();
@@ -391,6 +397,9 @@ private:
uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent);
uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent);
+ // P2P timeout in seconds
+ int64_t m_peer_connect_timeout;
+
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
std::vector<CSubNet> vWhitelistedRange;
@@ -400,12 +409,12 @@ private:
std::vector<ListenSocket> vhListenSocket;
std::atomic<bool> fNetworkActive;
- banmap_t setBanned;
+ banmap_t setBanned GUARDED_BY(cs_setBanned);
CCriticalSection cs_setBanned;
- bool setBannedIsDirty;
+ bool setBannedIsDirty GUARDED_BY(cs_setBanned);
bool fAddressesInitialized;
CAddrMan addrman;
- std::deque<std::string> vOneShots;
+ std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots);
CCriticalSection cs_vOneShots;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
@@ -540,7 +549,7 @@ struct LocalServiceInfo {
};
extern CCriticalSection cs_mapLocalHost;
-extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
+extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
class CNodeStats
@@ -630,23 +639,23 @@ class CNode
public:
// socket
std::atomic<ServiceFlags> nServices;
- SOCKET hSocket;
+ SOCKET hSocket GUARDED_BY(cs_hSocket);
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
- uint64_t nSendBytes;
- std::deque<std::vector<unsigned char>> vSendMsg;
+ uint64_t nSendBytes GUARDED_BY(cs_vSend);
+ std::deque<std::vector<unsigned char>> vSendMsg GUARDED_BY(cs_vSend);
CCriticalSection cs_vSend;
CCriticalSection cs_hSocket;
CCriticalSection cs_vRecv;
CCriticalSection cs_vProcessMsg;
- std::list<CNetMessage> vProcessMsg;
+ std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg);
size_t nProcessQueueSize;
CCriticalSection cs_sendProcessing;
std::deque<CInv> vRecvGetData;
- uint64_t nRecvBytes;
+ uint64_t nRecvBytes GUARDED_BY(cs_vRecv);
std::atomic<int> nRecvVersion;
std::atomic<int64_t> nLastSend;
@@ -662,7 +671,7 @@ public:
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
- std::string strSubVer, cleanSubVer;
+ std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
@@ -677,11 +686,11 @@ public:
// a) it allows us to not relay tx invs before receiving the peer's version message
// b) the peer may tell us in its version message that we should not relay tx invs
// unless it loads a bloom filter.
- bool fRelayTxes; //protected by cs_filter
+ bool fRelayTxes GUARDED_BY(cs_filter);
bool fSentAddr;
CSemaphoreGrant grantOutbound;
- CCriticalSection cs_filter;
- std::unique_ptr<CBloomFilter> pfilter;
+ mutable CCriticalSection cs_filter;
+ std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
std::atomic<int> nRefCount;
const uint64_t nKeyedNetGroup;
@@ -690,7 +699,7 @@ public:
protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
- mapMsgCmdSize mapRecvBytesPerMsgCmd;
+ mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
public:
uint256 hashContinue;
@@ -701,27 +710,26 @@ public:
CRollingBloomFilter addrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
- int64_t nNextAddrSend;
- int64_t nNextLocalAddrSend;
+ int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing);
+ int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing);
// inventory based relay
- CRollingBloomFilter filterInventoryKnown;
+ CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
// Set of transaction ids we still have to announce.
// They are sorted by the mempool before relay, so the order is not important.
std::set<uint256> setInventoryTxToSend;
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
- std::vector<uint256> vInventoryBlockToSend;
+ std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
CCriticalSection cs_inventory;
std::set<uint256> setAskFor;
std::multimap<int64_t, CInv> mapAskFor;
int64_t nNextInvSend;
// Used for headers announcements - unfiltered blocks to relay
- // Also protected by cs_inventory
- std::vector<uint256> vBlockHashesToAnnounce;
- // Used for BIP35 mempool sending, also protected by cs_inventory
- bool fSendMempool;
+ std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
+ // Used for BIP35 mempool sending
+ bool fSendMempool GUARDED_BY(cs_inventory);
// Last time a "MEMPOOL" request was serviced.
std::atomic<int64_t> timeLastMempoolReq;
@@ -742,7 +750,7 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued;
// Minimum fee rate with which to filter inv's to this node
- CAmount minFeeFilter;
+ CAmount minFeeFilter GUARDED_BY(cs_feeFilter);
CCriticalSection cs_feeFilter;
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
@@ -762,10 +770,10 @@ private:
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
- std::string addrName;
+ std::string addrName GUARDED_BY(cs_addrName);
// Our address, as reported by the peer
- CService addrLocal;
+ CService addrLocal GUARDED_BY(cs_addrLocal);
mutable CCriticalSection cs_addrLocal;
public:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f4ab3aa153..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/netbase.cpp b/src/netbase.cpp
index 6a750d5141..355e21d4e6 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -21,14 +21,18 @@
#include <codecvt>
#endif
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
// Settings
-static proxyType proxyInfo[NET_MAX];
-static proxyType nameProxy;
static CCriticalSection cs_proxyInfos;
+static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos);
+static proxyType nameProxy GUARDED_BY(cs_proxyInfos);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
@@ -264,11 +268,19 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
if (!IsSelectableSocket(hSocket)) {
return IntrRecvError::NetworkError;
}
- struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait));
+ int timeout_ms = std::min(endTime - curTime, maxWait);
+#ifdef USE_POLL
+ struct pollfd pollfd = {};
+ pollfd.fd = hSocket;
+ pollfd.events = POLLIN | POLLOUT;
+ int nRet = poll(&pollfd, 1, timeout_ms);
+#else
+ struct timeval tval = MillisToTimeval(timeout_ms);
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
+#endif
if (nRet == SOCKET_ERROR) {
return IntrRecvError::NetworkError;
}
@@ -499,11 +511,18 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
// WSAEINVAL is here because some legacy version of winsock uses it
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
{
+#ifdef USE_POLL
+ struct pollfd pollfd = {};
+ pollfd.fd = hSocket;
+ pollfd.events = POLLIN | POLLOUT;
+ int nRet = poll(&pollfd, 1, nTimeout);
+#else
struct timeval timeout = MillisToTimeval(nTimeout);
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
+#endif
if (nRet == 0)
{
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
diff --git a/src/noui.h b/src/noui.h
index 169c2bbd7f..79a79a9af2 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2014 The Bitcoin Core developers
+// Copyright (c) 2013-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 3afe6fe1b7..c49b9fa36b 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -511,7 +511,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
// of no harm to try to remove them again.
bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock)
{
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
if (pos != mapMemPoolTxs.end()) {
feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
@@ -548,7 +548,7 @@ CBlockPolicyEstimator::~CBlockPolicyEstimator()
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
{
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
unsigned int txHeight = entry.GetHeight();
uint256 hash = entry.GetTx().GetHash();
if (mapMemPoolTxs.count(hash)) {
@@ -615,7 +615,7 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
std::vector<const CTxMemPoolEntry*>& entries)
{
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
if (nBlockHeight <= nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random
// they don't affect the estimate.
@@ -693,7 +693,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
}
}
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
// Return failure if trying to analyze a target we're not tracking
if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())
return CFeeRate(0);
@@ -710,6 +710,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
{
+ LOCK(m_cs_fee_estimator);
switch (horizon) {
case FeeEstimateHorizon::SHORT_HALFLIFE: {
return shortStats->GetMaxConfirms();
@@ -819,7 +820,7 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget,
*/
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
{
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
if (feeCalc) {
feeCalc->desiredTarget = confTarget;
@@ -899,7 +900,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
{
try {
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
fileout << 149900; // version required to read: 0.14.99 or later
fileout << CLIENT_VERSION; // version that wrote the file
fileout << nBestSeenHeight;
@@ -924,7 +925,7 @@ bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
bool CBlockPolicyEstimator::Read(CAutoFile& filein)
{
try {
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
int nVersionRequired, nVersionThatWrote;
filein >> nVersionRequired >> nVersionThatWrote;
if (nVersionRequired > CLIENT_VERSION)
@@ -983,7 +984,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
void CBlockPolicyEstimator::FlushUnconfirmed() {
int64_t startclear = GetTimeMicros();
- LOCK(cs_feeEstimator);
+ LOCK(m_cs_fee_estimator);
size_t num_entries = mapMemPoolTxs.size();
// Remove every entry in mapMemPoolTxs
while (!mapMemPoolTxs.empty()) {
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 90f159b48c..c8472a12f5 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -228,10 +228,12 @@ public:
unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const;
private:
- unsigned int nBestSeenHeight;
- unsigned int firstRecordedHeight;
- unsigned int historicalFirst;
- unsigned int historicalBest;
+ mutable CCriticalSection m_cs_fee_estimator;
+
+ unsigned int nBestSeenHeight GUARDED_BY(m_cs_fee_estimator);
+ unsigned int firstRecordedHeight GUARDED_BY(m_cs_fee_estimator);
+ unsigned int historicalFirst GUARDED_BY(m_cs_fee_estimator);
+ unsigned int historicalBest GUARDED_BY(m_cs_fee_estimator);
struct TxStatsInfo
{
@@ -241,34 +243,32 @@ private:
};
// map of txids to information about that transaction
- std::map<uint256, TxStatsInfo> mapMemPoolTxs;
+ std::map<uint256, TxStatsInfo> mapMemPoolTxs GUARDED_BY(m_cs_fee_estimator);
/** Classes to track historical data on transaction confirmations */
- std::unique_ptr<TxConfirmStats> feeStats;
- std::unique_ptr<TxConfirmStats> shortStats;
- std::unique_ptr<TxConfirmStats> longStats;
-
- unsigned int trackedTxs;
- unsigned int untrackedTxs;
+ std::unique_ptr<TxConfirmStats> feeStats PT_GUARDED_BY(m_cs_fee_estimator);
+ std::unique_ptr<TxConfirmStats> shortStats PT_GUARDED_BY(m_cs_fee_estimator);
+ std::unique_ptr<TxConfirmStats> longStats PT_GUARDED_BY(m_cs_fee_estimator);
- std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
- std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
+ unsigned int trackedTxs GUARDED_BY(m_cs_fee_estimator);
+ unsigned int untrackedTxs GUARDED_BY(m_cs_fee_estimator);
- mutable CCriticalSection cs_feeEstimator;
+ std::vector<double> buckets GUARDED_BY(m_cs_fee_estimator); // The upper-bound of the range for the bucket (inclusive)
+ std::map<double, unsigned int> bucketMap GUARDED_BY(m_cs_fee_estimator); // Map of bucket upper-bound to index into all vectors by bucket
/** Process a transaction confirmed in a block*/
- bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
+ bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
/** Helper for estimateSmartFee */
- double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const;
+ double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
/** Helper for estimateSmartFee */
- double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const;
+ double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
/** Number of blocks of data recorded while fee estimates have been running */
- unsigned int BlockSpan() const;
+ unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
/** Number of blocks of recorded fee estimate data represented in saved data file */
- unsigned int HistoricalBlockSpan() const;
+ unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
/** Calculation of highest target that reasonable estimate can be provided for */
- unsigned int MaxUsableEstimate() const;
+ unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
};
class FeeFilterRounder
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 ef82351551..70255e058a 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,8 +56,8 @@
#include <QToolBar>
#include <QUrlQuery>
#include <QVBoxLayout>
+#include <QWindow>
-#include <boost/bind.hpp>
const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#if defined(Q_OS_MAC)
@@ -130,6 +129,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
if (QSystemTrayIcon::isSystemTrayAvailable()) {
createTrayIcon(networkStyle);
}
+ notificator = new Notificator(QApplication::applicationName(), trayIcon, this);
// Create status bar
statusBar();
@@ -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);
@@ -487,8 +537,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
OptionsModel* optionsModel = _clientModel->getOptionsModel();
- if(optionsModel)
- {
+ if (optionsModel && trayIcon) {
// be aware of the tray icon disable state change reported by the OptionsModel object.
connect(optionsModel, &OptionsModel::hideTrayIconChanged, this, &BitcoinGUI::setTrayIconVisible);
@@ -593,14 +642,10 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
assert(QSystemTrayIcon::isSystemTrayAvailable());
#ifndef Q_OS_MAC
- trayIcon = new QSystemTrayIcon(this);
+ trayIcon = new QSystemTrayIcon(networkStyle->getTrayAndWindowIcon(), this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText();
trayIcon->setToolTip(toolTip);
- trayIcon->setIcon(networkStyle->getTrayAndWindowIcon());
- trayIcon->hide();
#endif
-
- notificator = new Notificator(QApplication::applicationName(), trayIcon, this);
}
void BitcoinGUI::createTrayIconMenu()
@@ -1089,10 +1134,10 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
return false;
}
-void BitcoinGUI::setHDStatus(int hdEnabled)
+void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
{
- labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
+ labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
// eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
@@ -1138,7 +1183,7 @@ void BitcoinGUI::updateWalletStatus()
}
WalletModel * const walletModel = walletView->getWalletModel();
setEncryptionStatus(walletModel->getEncryptionStatus());
- setHDStatus(walletModel->wallet().hdEnabled());
+ setHDStatus(walletModel->privateKeysDisabled(), walletModel->wallet().hdEnabled());
}
#endif // ENABLE_WALLET
@@ -1226,9 +1271,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);
@@ -1247,8 +1289,8 @@ static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, co
void BitcoinGUI::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_message_box = m_node.handleMessageBox(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
- m_handler_question = m_node.handleQuestion(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
+ m_handler_message_box = m_node.handleMessageBox(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_handler_question = m_node.handleQuestion(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4));
}
void BitcoinGUI::unsubscribeFromCoreSignals()
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index e8b857c17c..aeff5dae30 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -223,7 +223,7 @@ private:
@param[in] hdEnabled current hd enabled status
@see WalletModel::EncryptionStatus
*/
- void setHDStatus(int hdEnabled);
+ void setHDStatus(bool privkeyDisabled, int hdEnabled);
public Q_SLOTS:
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 75012b279c..217326f818 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -252,13 +252,13 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig
void ClientModel::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
- m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(boost::bind(NotifyNumConnectionsChanged, this, _1));
- m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(boost::bind(NotifyNetworkActiveChanged, this, _1));
- m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(boost::bind(NotifyAlertChanged, this));
- m_handler_banned_list_changed = m_node.handleBannedListChanged(boost::bind(BannedListChanged, this));
- m_handler_notify_block_tip = m_node.handleNotifyBlockTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, false));
- m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, true));
+ m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
+ m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
+ m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
+ m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
+ m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
+ m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, false));
+ m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, true));
}
void ClientModel::unsubscribeFromCoreSignals()
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index ea970c0bc9..77f8bcf901 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -129,8 +129,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
- ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it
- ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
@@ -203,10 +201,10 @@ void CoinControlDialog::showMenu(const QPoint &point)
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
- if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
+ if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
- if (model->wallet().isLockedCoin(COutPoint(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())))
+ if (model->wallet().isLockedCoin(COutPoint(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
@@ -256,7 +254,7 @@ void CoinControlDialog::copyAddress()
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash()
{
- GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH));
+ GUIUtil::setClipboard(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString());
}
// context menu action: lock coin
@@ -265,7 +263,7 @@ void CoinControlDialog::lockCoin()
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
- COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
@@ -275,7 +273,7 @@ void CoinControlDialog::lockCoin()
// context menu action: unlock coin
void CoinControlDialog::unlockCoin()
{
- COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
@@ -371,9 +369,9 @@ void CoinControlDialog::radioListMode(bool checked)
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
{
- if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
+ if (column == COLUMN_CHECKBOX && item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
- COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
coinControl()->UnSelect(outpt);
@@ -693,10 +691,10 @@ void CoinControlDialog::updateView()
itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.depth_in_main_chain));
// transaction hash
- itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(output.hash.GetHex()));
+ itemOutput->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(output.hash.GetHex()));
// vout index
- itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(output.n));
+ itemOutput->setData(COLUMN_ADDRESS, VOutRole, output.n);
// disable locked coins
if (model->wallet().isLockedCoin(output))
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 9c3f6a46a2..8f15ae4b20 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -80,9 +80,14 @@ private:
COLUMN_ADDRESS,
COLUMN_DATE,
COLUMN_CONFIRMATIONS,
- COLUMN_TXHASH,
- COLUMN_VOUT_INDEX,
};
+
+ enum
+ {
+ TxHashRole = Qt::UserRole,
+ VOutRole
+ };
+
friend class CCoinControlWidgetItem;
private Q_SLOTS:
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index d1237ad283..bd7f3c5f56 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -402,7 +402,7 @@
<bool>false</bool>
</property>
<property name="columnCount">
- <number>10</number>
+ <number>6</number>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 8f34e6bc82..507d195b72 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -714,7 +714,7 @@
<item>
<widget class="QLabel" name="overriddenByCommandLineInfoLabel">
<property name="text">
- <string>Active command-line options that override above options:</string>
+ <string>Options set in this dialog are overridden by the command line or in the configuration file:</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 04b03a5435..4d6006c582 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -50,5 +50,6 @@ static const int MAX_URI_LENGTH = 255;
#define QAPP_ORG_DOMAIN "bitcoin.org"
#define QAPP_APP_NAME_DEFAULT "Bitcoin-Qt"
#define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet"
+#define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest"
#endif // BITCOIN_QT_GUICONSTANTS_H
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/networkstyle.cpp b/src/qt/networkstyle.cpp
index b134a139b0..da048d3211 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -17,7 +17,7 @@ static const struct {
} network_styles[] = {
{"main", QAPP_APP_NAME_DEFAULT, 0, 0, ""},
{"test", QAPP_APP_NAME_TESTNET, 70, 30, QT_TRANSLATE_NOOP("SplashScreen", "[testnet]")},
- {"regtest", QAPP_APP_NAME_TESTNET, 160, 30, "[regtest]"}
+ {"regtest", QAPP_APP_NAME_REGTEST, 160, 30, "[regtest]"}
};
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 94505043dc..4b91c19761 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -39,7 +39,7 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon
mode(None),
trayIcon(_trayIcon)
#ifdef USE_DBUS
- ,interface(0)
+ ,interface(nullptr)
#endif
{
if(_trayIcon && _trayIcon->supportsMessages())
@@ -154,14 +154,14 @@ QVariant FreedesktopImage::toVariant(const QImage &img)
void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
- Q_UNUSED(cls);
- // Arguments for DBus call:
+ // https://developer.gnome.org/notification-spec/
+ // Arguments for DBus "Notify" call:
QList<QVariant> args;
// Program Name:
args.append(programName);
- // Unique ID of this notification type:
+ // Replaces ID; A value of 0 means that this notification won't replace any existing notifications:
args.append(0U);
// Application Icon, empty string
@@ -209,9 +209,8 @@ void Notificator::notifyDBus(Class cls, const QString &title, const QString &tex
}
#endif
-void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
+void Notificator::notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout)
{
- Q_UNUSED(icon);
QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
switch(cls) // Set icon based on class
{
@@ -222,13 +221,12 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString &
trayIcon->showMessage(title, text, sicon, millisTimeout);
}
-// Based on Qt's tray icon implementation
#ifdef Q_OS_MAC
-void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) {
+void Notificator::notifyMacUserNotificationCenter(const QString &title, const QString &text)
+{
// icon is not supported by the user notification center yet. OSX will use the app icon.
MacNotificationHandler::instance()->showNotification(title, text);
}
-
#endif
void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
@@ -241,11 +239,11 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
break;
#endif
case QSystemTray:
- notifySystray(cls, title, text, icon, millisTimeout);
+ notifySystray(cls, title, text, millisTimeout);
break;
#ifdef Q_OS_MAC
case UserNotificationCenter:
- notifyMacUserNotificationCenter(cls, title, text, icon);
+ notifyMacUserNotificationCenter(title, text);
break;
#endif
default:
diff --git a/src/qt/notificator.h b/src/qt/notificator.h
index 10b7b6c50a..7d80b43e43 100644
--- a/src/qt/notificator.h
+++ b/src/qt/notificator.h
@@ -57,7 +57,7 @@ private:
enum Mode {
None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */
Freedesktop, /**< Use DBus org.freedesktop.Notifications */
- QSystemTray, /**< Use QSystemTray::showMessage */
+ QSystemTray, /**< Use QSystemTrayIcon::showMessage() */
UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */
};
QString programName;
@@ -68,9 +68,9 @@ private:
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
#endif
- void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
+ void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout);
#ifdef Q_OS_MAC
- void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon);
+ void notifyMacUserNotificationCenter(const QString &title, const QString &text);
#endif
};
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 1db9609979..bec79335e7 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -161,15 +161,21 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
{
int unit = walletModel->getOptionsModel()->getDisplayUnit();
m_balances = balances;
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
-
+ if (walletModel->privateKeysDisabled()) {
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ } else {
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ }
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
bool showImmature = balances.immature_balance != 0;
@@ -178,7 +184,7 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
// for symmetry reasons also show immature label when the watch-only one is shown
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature);
- ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance
+ ui->labelWatchImmature->setVisible(!walletModel->privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance
}
// show/hide watch-only labels
@@ -231,8 +237,10 @@ void OverviewPage::setWalletModel(WalletModel *model)
connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit);
- updateWatchOnlyLabels(wallet.haveWatchOnly());
- connect(model, &WalletModel::notifyWatchonlyChanged, this, &OverviewPage::updateWatchOnlyLabels);
+ updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->privateKeysDisabled());
+ connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) {
+ updateWatchOnlyLabels(showWatchOnly && !walletModel->privateKeysDisabled());
+ });
}
// update the display unit, to not use the default ("BTC")
diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/movies/makespinner.sh
index 76e36e4f31..f47c66e02c 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/movies/makespinner.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+#
# Copyright (c) 2014-2015 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/qt/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..b109a08b1c 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -24,7 +24,6 @@
#include <QPainter>
#include <QRadialGradient>
-#include <boost/bind.hpp>
SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
QWidget(0, f), curAlignment(0), m_node(node)
@@ -145,10 +144,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())
@@ -176,7 +173,7 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
#ifdef ENABLE_WALLET
void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
- m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2, false)));
+ m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
m_connected_wallets.emplace_back(std::move(wallet));
}
#endif
@@ -184,8 +181,8 @@ void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_init_message = m_node.handleInitMessage(boost::bind(InitMessage, this, _1));
- m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2, _3));
+ m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
+ m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
#ifdef ENABLE_WALLET
m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
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/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 1983c3bc92..01722146c5 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -27,7 +27,6 @@
#include <QIcon>
#include <QList>
-#include <boost/bind.hpp>
// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
@@ -745,8 +744,8 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i
void TransactionTableModel::subscribeToCoreSignals()
{
// Connect signals to wallet
- m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
- m_handler_show_progress = walletModel->wallet().handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
+ m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
+ m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
}
void TransactionTableModel::unsubscribeFromCoreSignals()
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 05ca569d7a..1963e89b24 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 353da0c9b4..ba16fdd5e1 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -428,12 +428,12 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly
void WalletModel::subscribeToCoreSignals()
{
// Connect signals to wallet
- m_handler_unload = m_wallet->handleUnload(boost::bind(&NotifyUnload, this));
- m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this));
- m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
- m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
- m_handler_show_progress = m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
- m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(boost::bind(NotifyWatchonlyChanged, this, _1));
+ m_handler_unload = m_wallet->handleUnload(std::bind(&NotifyUnload, this));
+ m_handler_status_changed = m_wallet->handleStatusChanged(std::bind(&NotifyKeyStoreStatusChanged, this));
+ m_handler_address_book_changed = m_wallet->handleAddressBookChanged(std::bind(NotifyAddressBookChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
+ m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
+ m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
+ m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1));
}
void WalletModel::unsubscribeFromCoreSignals()
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/rest.cpp b/src/rest.cpp
index 4988e6ed26..4f26e3afb5 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -136,10 +136,12 @@ static bool rest_headers(HTTPRequest* req,
if (!ParseHashStr(hashStr, hash))
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
+ const CBlockIndex* tip = nullptr;
std::vector<const CBlockIndex *> headers;
headers.reserve(count);
{
LOCK(cs_main);
+ tip = chainActive.Tip();
const CBlockIndex* pindex = LookupBlockIndex(hash);
while (pindex != nullptr && chainActive.Contains(pindex)) {
headers.push_back(pindex);
@@ -175,11 +177,8 @@ static bool rest_headers(HTTPRequest* req,
}
case RetFormat::JSON: {
UniValue jsonHeaders(UniValue::VARR);
- {
- LOCK(cs_main);
- for (const CBlockIndex *pindex : headers) {
- jsonHeaders.push_back(blockheaderToJSON(pindex));
- }
+ for (const CBlockIndex *pindex : headers) {
+ jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
}
std::string strJSON = jsonHeaders.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -207,8 +206,10 @@ static bool rest_block(HTTPRequest* req,
CBlock block;
CBlockIndex* pblockindex = nullptr;
+ CBlockIndex* tip = nullptr;
{
LOCK(cs_main);
+ tip = chainActive.Tip();
pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
@@ -241,11 +242,7 @@ static bool rest_block(HTTPRequest* req,
}
case RetFormat::JSON: {
- UniValue objBlock;
- {
- LOCK(cs_main);
- objBlock = blockToJSON(block, pblockindex, showTxDetails);
- }
+ UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e3d9357358..55282f433f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -59,10 +59,7 @@ static CUpdatedBlock latestblock;
*/
double GetDifficulty(const CBlockIndex* blockindex)
{
- if (blockindex == nullptr)
- {
- return 1.0;
- }
+ assert(blockindex);
int nShift = (blockindex->nBits >> 24) & 0xff;
double dDiff =
@@ -82,15 +79,22 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
-UniValue blockheaderToJSON(const CBlockIndex* blockindex)
+static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* blockindex, const CBlockIndex*& next)
+{
+ next = tip->GetAncestor(blockindex->nHeight + 1);
+ if (next && next->pprev == blockindex) {
+ return tip->nHeight - blockindex->nHeight + 1;
+ }
+ next = nullptr;
+ return blockindex == tip ? 1 : -1;
+}
+
+UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex)
{
- AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
- int confirmations = -1;
- // Only report confirmations if the block is on the main chain
- if (chainActive.Contains(blockindex))
- confirmations = chainActive.Height() - blockindex->nHeight + 1;
+ const CBlockIndex* pnext;
+ int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
result.pushKV("confirmations", confirmations);
result.pushKV("height", blockindex->nHeight);
result.pushKV("version", blockindex->nVersion);
@@ -106,21 +110,17 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
- CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails)
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
- AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
- int confirmations = -1;
- // Only report confirmations if the block is on the main chain
- if (chainActive.Contains(blockindex))
- confirmations = chainActive.Height() - blockindex->nHeight + 1;
+ const CBlockIndex* pnext;
+ int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
@@ -152,7 +152,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
- CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
@@ -212,11 +211,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 +251,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 +296,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 +487,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 +523,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 +589,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 +655,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 +690,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 +718,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"
@@ -792,7 +768,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
return strHex;
}
- return blockheaderToJSON(pblockindex);
+ return blockheaderToJSON(chainActive.Tip(), pblockindex);
}
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
@@ -823,13 +799,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"
@@ -897,7 +870,7 @@ static UniValue getblock(const JSONRPCRequest& request)
return strHex;
}
- return blockToJSON(block, pblockindex, verbosity >= 2);
+ return blockToJSON(block, chainActive.Tip(), pblockindex, verbosity >= 2);
}
struct CCoinsStats
@@ -977,12 +950,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 +1046,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 +1127,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"
@@ -1186,7 +1149,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
}
/** Implementation of IsSuperMajority with better feedback */
-static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
+static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
UniValue rv(UniValue::VOBJ);
bool activated = false;
@@ -1206,7 +1169,7 @@ static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Con
return rv;
}
-static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
+static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
UniValue rv(UniValue::VOBJ);
rv.pushKV("id", name);
@@ -1313,20 +1276,21 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
LOCK(cs_main);
+ const CBlockIndex* tip = chainActive.Tip();
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", Params().NetworkIDString());
obj.pushKV("blocks", (int)chainActive.Height());
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
- obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex());
- obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
- obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast());
- obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()));
+ obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
+ obj.pushKV("difficulty", (double)GetDifficulty(tip));
+ obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
+ obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", IsInitialBlockDownload());
- obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex());
+ obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
if (fPruneMode) {
- CBlockIndex* block = chainActive.Tip();
+ const CBlockIndex* block = tip;
assert(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
@@ -1343,7 +1307,6 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
}
const Consensus::Params& consensusParams = Params().GetConsensus();
- CBlockIndex* tip = chainActive.Tip();
UniValue softforks(UniValue::VARR);
UniValue bip9_softforks(UniValue::VOBJ);
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
@@ -1458,7 +1421,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 +1489,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 +1526,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 +1567,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 +1605,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 +1739,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 +2099,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"
@@ -2187,6 +2124,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
" \"txid\" : \"transactionid\", (string) The transaction id\n"
" \"vout\": n, (numeric) the vout value\n"
" \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
" \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
" \"height\" : n, (numeric) Height of the unspent transaction output\n"
" }\n"
@@ -2221,6 +2159,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
}
std::set<CScript> needles;
+ std::map<CScript, std::string> descriptors;
CAmount total_in = 0;
// loop through the scan objects
@@ -2253,7 +2192,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
if (!desc->Expand(i, provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
}
- needles.insert(scripts.begin(), scripts.end());
+ for (const auto& script : scripts) {
+ std::string inferred = InferDescriptor(script, provider)->ToString();
+ needles.emplace(script);
+ descriptors.emplace(std::move(script), std::move(inferred));
+ }
}
}
@@ -2286,6 +2229,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
unspent.pushKV("txid", outpoint.hash.GetHex());
unspent.pushKV("vout", (int32_t)outpoint.n);
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
unspent.pushKV("height", (int32_t)coin.nHeight);
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index add335eb8a..529132d033 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -27,7 +27,7 @@ double GetDifficulty(const CBlockIndex* blockindex);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
/** Block description to JSON */
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
/** Mempool information to JSON */
UniValue mempoolInfoToJSON();
@@ -36,7 +36,7 @@ UniValue mempoolInfoToJSON();
UniValue mempoolToJSON(bool fVerbose = false);
/** Block header to JSON */
-UniValue blockheaderToJSON(const CBlockIndex* blockindex);
+UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
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..2fd6f99be5 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 */ false, /* 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 */ false, /* 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"
@@ -529,21 +503,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
- // If the caller is indicating segwit support, then allow CreateNewBlock()
- // to select witness transactions, after segwit activates (otherwise
- // don't).
- bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end();
+ // GBT must be called with 'segwit' set in the rules
+ if (setClientRules.count(segwit_info.name) != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
+ }
// Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
- // Cache whether the last invocation was with segwit support, to avoid returning
- // a segwit-block to a non-segwit caller.
- static bool fLastTemplateSupportsSegwit = true;
if (pindexPrev != chainActive.Tip() ||
- (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) ||
- fLastTemplateSupportsSegwit != fSupportsSegwit)
+ (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = nullptr;
@@ -552,11 +522,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrevNew = chainActive.Tip();
nStart = GetTime();
- fLastTemplateSupportsSegwit = fSupportsSegwit;
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit);
+ pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
@@ -708,7 +677,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("bits", strprintf("%08x", pblock->nBits));
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
- if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) {
+ if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
}
@@ -742,13 +711,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 +776,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 +816,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 +825,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 +882,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 a3ed4e86d9..bf00870107 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"
@@ -135,8 +130,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
" ],\n"
" \"blockhash\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The confirmations\n"
- " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"time\" : ttt, (numeric) Same as \"blocktime\"\n"
"}\n"
"\nExamples:\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},
- {"privkyes", 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 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"
@@ -1164,10 +1135,9 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"Sign the transaction, and get back the hex\n"
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
"\nTest acceptance of the transaction (signed hex)\n"
- + HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
+ + 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..e7e047334e 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -15,7 +15,6 @@
#include <util/strencodings.h>
#include <util/system.h>
-#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
@@ -24,7 +23,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 +141,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 +202,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 +220,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 +231,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 +255,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 +302,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 +310,7 @@ void InterruptRPC()
{
LogPrint(BCLog::RPC, "Interrupting RPC\n");
// Interrupt e.g. running longpolls
- fRPCRunning = false;
+ g_rpc_running = false;
}
void StopRPC()
@@ -327,7 +323,7 @@ void StopRPC()
bool IsRPCRunning()
{
- return fRPCRunning;
+ return g_rpc_running;
}
void SetRPCWarmupStatus(const std::string& newStatus)
@@ -507,11 +503,7 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
std::vector<std::string> CRPCTable::listCommands() const
{
std::vector<std::string> commandList;
- typedef std::map<std::string, const CRPCCommand*> commandMap;
-
- std::transform( mapCommands.begin(), mapCommands.end(),
- std::back_inserter(commandList),
- boost::bind(&commandMap::value_type::first,_1) );
+ for (const auto& i : mapCommands) commandList.emplace_back(i.first);
return commandList;
}
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/scheduler.cpp b/src/scheduler.cpp
index 552391d7d0..b2da62fc75 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -8,7 +8,6 @@
#include <reverselock.h>
#include <assert.h>
-#include <boost/bind.hpp>
#include <utility>
CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false)
@@ -120,12 +119,12 @@ void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSecon
static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds)
{
f();
- s->scheduleFromNow(boost::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds);
+ s->scheduleFromNow(std::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds);
}
void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds)
{
- scheduleFromNow(boost::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds);
+ scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds);
}
size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
diff --git a/src/scheduler.h b/src/scheduler.h
index 6c45f508ec..6d7f42cf9f 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -25,7 +25,7 @@
// CScheduler* s = new CScheduler();
// s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { }
// s->scheduleFromNow(std::bind(Class::func, this, argument), 3);
-// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue, s));
+// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s));
//
// ... then at program shutdown, clean up the thread running serviceQueue:
// t->interrupt();
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index d343972c40..a702be5b78 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -40,8 +40,8 @@ struct PubkeyProvider
{
virtual ~PubkeyProvider() = default;
- /** Derive a public key. */
- virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const = 0;
+ /** Derive a public key. If key==nullptr, only info is desired. */
+ virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
@@ -68,7 +68,7 @@ class OriginPubkeyProvider final : public PubkeyProvider
public:
OriginPubkeyProvider(KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : m_origin(std::move(info)), m_provider(std::move(provider)) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
@@ -94,9 +94,9 @@ class ConstPubkeyProvider final : public PubkeyProvider
public:
ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
- key = m_pubkey;
+ if (key) *key = m_pubkey;
info.path.clear();
CKeyID keyid = m_pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
@@ -152,26 +152,28 @@ public:
BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
{
- if (IsHardened()) {
- CExtKey extkey;
- if (!GetExtKey(arg, extkey)) return false;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
+ if (key) {
+ if (IsHardened()) {
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ *key = extkey.Neuter().pubkey;
+ } else {
+ // TODO: optimize by caching
+ CExtPubKey extkey = m_extkey;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ assert(m_derive != DeriveType::HARDENED);
+ *key = extkey.pubkey;
}
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
- key = extkey.Neuter().pubkey;
- } else {
- // TODO: optimize by caching
- CExtPubKey extkey = m_extkey;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
- }
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- assert(m_derive != DeriveType::HARDENED);
- key = extkey.pubkey;
}
CKeyID keyid = m_extkey.pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
@@ -202,124 +204,119 @@ public:
}
};
-/** A parsed addr(A) descriptor. */
-class AddressDescriptor final : public Descriptor
+/** Base class for all Descriptor implementations. */
+class DescriptorImpl : public Descriptor
{
- CTxDestination m_destination;
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size of Multisig).
+ const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
+ //! The sub-descriptor argument (nullptr for everything but SH and WSH).
+ const std::unique_ptr<DescriptorImpl> m_script_arg;
+ //! The string name of the descriptor function.
+ const std::string m_name;
+
+protected:
+ //! Return a serialization of anything except pubkey and script arguments, to be prepended to those.
+ virtual std::string ToStringExtra() const { return ""; }
+
+ /** A helper function to construct the scripts for this descriptor.
+ *
+ * This function is invoked once for every CScript produced by evaluating
+ * m_script_arg, or just once in case m_script_arg is nullptr.
+
+ * @param pubkeys The evaluations of the m_pubkey_args field.
+ * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
+ * @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
+ * The script and pubkeys argument to this function are automatically added.
+ * @return A vector with scriptPubKeys for this descriptor.
+ */
+ virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), m_name(name) {}
- bool IsRange() const override { return false; }
- std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+ bool IsSolvable() const override
{
- output_scripts = std::vector<CScript>{GetScriptForDestination(m_destination)};
- return true;
- }
-};
-
-/** A parsed raw(H) descriptor. */
-class RawDescriptor final : public Descriptor
-{
- CScript m_script;
-
-public:
- RawDescriptor(CScript script) : m_script(std::move(script)) {}
-
- bool IsRange() const override { return false; }
- std::string ToString() const override { return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- output_scripts = std::vector<CScript>{m_script};
- return true;
- }
-};
-
-/** A parsed pk(P), pkh(P), or wpkh(P) descriptor. */
-class SingleKeyDescriptor final : public Descriptor
-{
- const std::function<CScript(const CPubKey&)> m_script_fn;
- const std::string m_fn_name;
- std::unique_ptr<PubkeyProvider> m_provider;
-
-public:
- SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {}
-
- bool IsRange() const override { return m_provider->IsRange(); }
- std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_provider->ToPrivateString(arg, ret)) return false;
- out = m_fn_name + "(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- CPubKey key;
- KeyOriginInfo info;
- if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
- output_scripts = std::vector<CScript>{m_script_fn(key)};
- out.origins.emplace(key.GetID(), std::move(info));
- out.pubkeys.emplace(key.GetID(), key);
+ if (m_script_arg) {
+ if (!m_script_arg->IsSolvable()) return false;
+ }
return true;
}
-};
-
-CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); }
-CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); }
-CScript P2WPKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID())); }
-
-/** A parsed multi(...) descriptor. */
-class MultisigDescriptor : public Descriptor
-{
- int m_threshold;
- std::vector<std::unique_ptr<PubkeyProvider>> m_providers;
-
-public:
- MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : m_threshold(threshold), m_providers(std::move(providers)) {}
- bool IsRange() const override
+ bool IsRange() const final
{
- for (const auto& p : m_providers) {
- if (p->IsRange()) return true;
+ for (const auto& pubkey : m_pubkey_args) {
+ if (pubkey->IsRange()) return true;
+ }
+ if (m_script_arg) {
+ if (m_script_arg->IsRange()) return true;
}
return false;
}
- std::string ToString() const override
+ bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv) const
{
- std::string ret = strprintf("multi(%i", m_threshold);
- for (const auto& p : m_providers) {
- ret += "," + p->ToString();
+ std::string extra = ToStringExtra();
+ size_t pos = extra.size() > 0 ? 1 : 0;
+ std::string ret = m_name + "(" + extra;
+ for (const auto& pubkey : m_pubkey_args) {
+ if (pos++) ret += ",";
+ std::string tmp;
+ if (priv) {
+ if (!pubkey->ToPrivateString(*arg, tmp)) return false;
+ } else {
+ tmp = pubkey->ToString();
+ }
+ ret += std::move(tmp);
}
- return std::move(ret) + ")";
- }
-
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret = strprintf("multi(%i", m_threshold);
- for (const auto& p : m_providers) {
- std::string sub;
- if (!p->ToPrivateString(arg, sub)) return false;
- ret += "," + std::move(sub);
+ if (m_script_arg) {
+ if (pos++) ret += ",";
+ std::string tmp;
+ if (!m_script_arg->ToStringHelper(arg, tmp, priv)) return false;
+ ret += std::move(tmp);
}
out = std::move(ret) + ")";
return true;
}
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+ std::string ToString() const final
+ {
+ std::string ret;
+ ToStringHelper(nullptr, ret, false);
+ return ret;
+ }
+
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final { return ToStringHelper(&arg, out, true); }
+
+ bool ExpandHelper(int pos, const SigningProvider& arg, Span<const unsigned char>* cache_read, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache_write) const
{
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
- entries.reserve(m_providers.size());
- // Construct temporary data in `entries`, to avoid producing output in case of failure.
- for (const auto& p : m_providers) {
+ entries.reserve(m_pubkey_args.size());
+
+ // Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
+ for (const auto& p : m_pubkey_args) {
entries.emplace_back();
- if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second)) return false;
+ if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
+ if (cache_read) {
+ // Cached expanded public key exists, use it.
+ if (cache_read->size() == 0) return false;
+ bool compressed = ((*cache_read)[0] == 0x02 || (*cache_read)[0] == 0x03) && cache_read->size() >= 33;
+ bool uncompressed = ((*cache_read)[0] == 0x04) && cache_read->size() >= 65;
+ if (!(compressed || uncompressed)) return false;
+ CPubKey pubkey(cache_read->begin(), cache_read->begin() + (compressed ? 33 : 65));
+ entries.back().first = pubkey;
+ *cache_read = cache_read->subspan(compressed ? 33 : 65);
+ }
+ if (cache_write) {
+ cache_write->insert(cache_write->end(), entries.back().first.begin(), entries.back().first.end());
+ }
}
+ std::vector<CScript> subscripts;
+ if (m_script_arg) {
+ FlatSigningProvider subprovider;
+ if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
+ out = Merge(out, subprovider);
+ }
+
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
for (auto& entry : entries) {
@@ -327,87 +324,141 @@ public:
out.origins.emplace(entry.first.GetID(), std::move(entry.second));
out.pubkeys.emplace(entry.first.GetID(), entry.first);
}
- output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
+ if (m_script_arg) {
+ for (const auto& subscript : subscripts) {
+ out.scripts.emplace(CScriptID(subscript), subscript);
+ std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
+ for (auto& addscript : addscripts) {
+ output_scripts.push_back(std::move(addscript));
+ }
+ }
+ } else {
+ output_scripts = MakeScripts(pubkeys, nullptr, out);
+ }
return true;
}
+
+ bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const final
+ {
+ return ExpandHelper(pos, provider, nullptr, output_scripts, out, cache);
+ }
+
+ bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const final
+ {
+ Span<const unsigned char> span = MakeSpan(cache);
+ return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
+ }
};
-/** A parsed sh(S) or wsh(S) descriptor. */
-class ConvertorDescriptor : public Descriptor
+/** Construct a vector with one element, which is moved into it. */
+template<typename T>
+std::vector<T> Singleton(T elem)
{
- const std::function<CScript(const CScript&)> m_convert_fn;
- const std::string m_fn_name;
- std::unique_ptr<Descriptor> m_descriptor;
+ std::vector<T> ret;
+ ret.emplace_back(std::move(elem));
+ return ret;
+}
+/** A parsed addr(A) descriptor. */
+class AddressDescriptor final : public DescriptorImpl
+{
+ const CTxDestination m_destination;
+protected:
+ std::string ToStringExtra() const override { return EncodeDestination(m_destination); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(m_destination)); }
public:
- ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {}
+ AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
+ bool IsSolvable() const final { return false; }
+};
- bool IsRange() const override { return m_descriptor->IsRange(); }
- std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_descriptor->ToPrivateString(arg, ret)) return false;
- out = m_fn_name + "(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
- {
- std::vector<CScript> sub;
- if (!m_descriptor->Expand(pos, arg, sub, out)) return false;
- output_scripts.clear();
- for (const auto& script : sub) {
- CScriptID id(script);
- out.scripts.emplace(CScriptID(script), script);
- output_scripts.push_back(m_convert_fn(script));
- }
- return true;
- }
+/** A parsed raw(H) descriptor. */
+class RawDescriptor final : public DescriptorImpl
+{
+ const CScript m_script;
+protected:
+ std::string ToStringExtra() const override { return HexStr(m_script.begin(), m_script.end()); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(m_script); }
+public:
+ RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
+ bool IsSolvable() const final { return false; }
};
-CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); }
-CScript ConvertP2WSH(const CScript& script) { return GetScriptForDestination(WitnessV0ScriptHash(script)); }
+/** A parsed pk(P) descriptor. */
+class PKDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForRawPubKey(keys[0])); }
+public:
+ PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pk") {}
+};
-/** A parsed combo(P) descriptor. */
-class ComboDescriptor final : public Descriptor
+/** A parsed pkh(P) descriptor. */
+class PKHDescriptor final : public DescriptorImpl
{
- std::unique_ptr<PubkeyProvider> m_provider;
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(keys[0].GetID())); }
+public:
+ PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
+};
+/** A parsed wpkh(P) descriptor. */
+class WPKHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0KeyHash(keys[0].GetID()))); }
public:
- ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {}
+ WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "wpkh") {}
+};
- bool IsRange() const override { return m_provider->IsRange(); }
- std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; }
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
- {
- std::string ret;
- if (!m_provider->ToPrivateString(arg, ret)) return false;
- out = "combo(" + std::move(ret) + ")";
- return true;
- }
- bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
+/** A parsed combo(P) descriptor. */
+class ComboDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
{
- CPubKey key;
- KeyOriginInfo info;
- if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
- CKeyID keyid = key.GetID();
- {
- CScript p2pk = GetScriptForRawPubKey(key);
- CScript p2pkh = GetScriptForDestination(keyid);
- output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
- out.pubkeys.emplace(keyid, key);
- out.origins.emplace(keyid, std::move(info));
+ std::vector<CScript> ret;
+ CKeyID id = keys[0].GetID();
+ ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK
+ ret.emplace_back(GetScriptForDestination(id)); // P2PKH
+ if (keys[0].IsCompressed()) {
+ CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(id));
+ out.scripts.emplace(CScriptID(p2wpkh), p2wpkh);
+ ret.emplace_back(p2wpkh);
+ ret.emplace_back(GetScriptForDestination(CScriptID(p2wpkh))); // P2SH-P2WPKH
}
- if (key.IsCompressed()) {
- CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid));
- CScriptID p2wpkh_id(p2wpkh);
- CScript p2sh_p2wpkh = GetScriptForDestination(p2wpkh_id);
- out.scripts.emplace(p2wpkh_id, p2wpkh);
- output_scripts.push_back(std::move(p2wpkh));
- output_scripts.push_back(std::move(p2sh_p2wpkh));
- }
- return true;
+ return ret;
}
+public:
+ ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "combo") {}
+};
+
+/** A parsed multi(...) descriptor. */
+class MultisigDescriptor final : public DescriptorImpl
+{
+ const int m_threshold;
+protected:
+ std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForMultisig(m_threshold, keys)); }
+public:
+ MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : DescriptorImpl(std::move(providers), {}, "multi"), m_threshold(threshold) {}
+};
+
+/** A parsed sh(...) descriptor. */
+class SHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(CScriptID(*script))); }
+public:
+ SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
+};
+
+/** A parsed wsh(...) descriptor. */
+class WSHDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
+public:
+ WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
};
////////////////////////////////////////////////////////////////////////////
@@ -555,18 +606,18 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool per
}
/** Parse a script in a particular context. */
-std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
+std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
{
auto expr = Expr(sp);
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk");
+ return MakeUnique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKHGetScript, "pkh");
+ return MakeUnique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(expr, true, out);
@@ -599,17 +650,17 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(expr, false, out);
if (!pubkey) return nullptr;
- return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2WPKHGetScript, "wpkh");
+ return MakeUnique<WPKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2SH, "sh");
+ return MakeUnique<SHDescriptor>(std::move(desc));
}
if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2WSH, "wsh");
+ return MakeUnique<WSHDescriptor>(std::move(desc));
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
@@ -625,12 +676,91 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
return nullptr;
}
+std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
+{
+ std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
+ return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(key_provider));
+ }
+ return key_provider;
+}
+
+std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ std::vector<std::vector<unsigned char>> data;
+ txnouttype txntype = Solver(script, data);
+
+ if (txntype == TX_PUBKEY) {
+ CPubKey pubkey(data[0].begin(), data[0].end());
+ if (pubkey.IsValid()) {
+ return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_PUBKEYHASH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ }
+ }
+ if (txntype == TX_MULTISIG) {
+ std::vector<std::unique_ptr<PubkeyProvider>> providers;
+ for (size_t i = 1; i + 1 < data.size(); ++i) {
+ CPubKey pubkey(data[i].begin(), data[i].end());
+ providers.push_back(InferPubkey(pubkey, ctx, provider));
+ }
+ return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
+ }
+ if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
+ uint160 hash(data[0]);
+ CScriptID scriptid(hash);
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
+ if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
+ }
+ }
+ if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ CScriptID scriptid;
+ CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
+ if (sub) return MakeUnique<WSHDescriptor>(std::move(sub));
+ }
+ }
+
+ CTxDestination dest;
+ if (ExtractDestination(script, dest)) {
+ if (GetScriptForDestination(dest) == script) {
+ return MakeUnique<AddressDescriptor>(std::move(dest));
+ }
+ }
+
+ return MakeUnique<RawDescriptor>(script);
+}
+
} // namespace
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
{
Span<const char> sp(descriptor.data(), descriptor.size());
auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
- if (sp.size() == 0 && ret) return ret;
+ if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
}
+
+std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
+{
+ return InferScript(script, ParseScriptContext::TOP, provider);
+}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 87e07369c7..44f0efca03 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -32,6 +32,10 @@ struct Descriptor {
/** Whether the expansion of this descriptor depends on the position. */
virtual bool IsRange() const = 0;
+ /** Whether this descriptor has all information about signing ignoring lack of private keys.
+ * This is true for all descriptors except ones that use `raw` or `addr` constructions. */
+ virtual bool IsSolvable() const = 0;
+
/** Convert the descriptor back to a string, undoing parsing. */
virtual std::string ToString() const = 0;
@@ -44,12 +48,37 @@ struct Descriptor {
* provider: the provider to query for private keys in case of hardened derivation.
* output_script: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
+ * cache: vector which will be overwritten with cache data necessary to-evaluate the descriptor at this point without access to private keys.
*/
- virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
+ virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
+
+ /** Expand a descriptor at a specified position using cached expansion data.
+ *
+ * pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
+ * cache: vector from which cached expansion data will be read.
+ * output_script: the expanded scriptPubKeys will be put here.
+ * out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
+ */
+ virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
};
/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
-#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
+/** Find a descriptor for the specified script, using information from provider where possible.
+ *
+ * A non-ranged descriptor which only generates the specified script will be returned in all
+ * circumstances.
+ *
+ * For public keys with key origin information, this information will be preserved in the returned
+ * descriptor.
+ *
+ * - If all information for solving `script` is present in `provider`, a descriptor will be returned
+ * which is `IsSolvable()` and encapsulates said information.
+ * - Failing that, if `script` corresponds to a known address type, an "addr()" descriptor will be
+ * returned (which is not `IsSolvable()`).
+ * - Failing that, a "raw()" descriptor is returned.
+ */
+std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider);
+#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 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/script/sign.h b/src/script/sign.h
index a478f49789..20c7203b26 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -24,6 +24,11 @@ struct KeyOriginInfo
{
unsigned char fingerprint[4];
std::vector<uint32_t> path;
+
+ friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
+ }
};
/** An interface to be implemented by keystores that support signing. */
diff --git a/src/streams.h b/src/streams.h
index 49a7303d47..0809c96be1 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -64,12 +64,6 @@ public:
size_t size() const { return stream->size(); }
};
-template<typename S>
-OverrideStream<S> WithOrVersion(S* s, int nVersionFlag)
-{
- return OverrideStream<S>(s, s->GetType(), s->GetVersion() | nVersionFlag);
-}
-
/* Minimal stream for overwriting and/or appending to an existing byte vector
*
* The referenced vector will grow as necessary
@@ -749,7 +743,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;
}
@@ -834,7 +828,7 @@ public:
// prevent reading beyond a certain position
// no argument removes the limit
- bool SetLimit(uint64_t nPos = (uint64_t)(-1)) {
+ bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {
if (nPos < nReadPos)
return false;
nReadLimit = nPos;
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 8c2873d916..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).GetHash().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/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 7d8ae46fb8..b61152985f 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -68,11 +68,4 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
TestDifficulty(0x12345678, 5913134931067755359633408.0);
}
-// Verify that difficulty is 1.0 for an empty chain.
-BOOST_AUTO_TEST_CASE(get_difficulty_for_null_tip)
-{
- double difficulty = GetDifficulty(nullptr);
- RejectDifficultyMismatch(difficulty, 1.0);
-}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 2144202b8d..625362f446 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(gcsfilter_test)
excluded_elements.insert(std::move(element2));
}
- GCSFilter filter(0, 0, 10, 1 << 10, included_elements);
+ GCSFilter filter({0, 0, 10, 1 << 10}, included_elements);
for (const auto& element : included_elements) {
BOOST_CHECK(filter.Match(element));
@@ -39,6 +39,19 @@ BOOST_AUTO_TEST_CASE(gcsfilter_test)
}
}
+BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor)
+{
+ GCSFilter filter;
+ BOOST_CHECK_EQUAL(filter.GetN(), 0);
+ BOOST_CHECK_EQUAL(filter.GetEncoded().size(), 1);
+
+ const GCSFilter::Params& params = filter.GetParams();
+ BOOST_CHECK_EQUAL(params.m_siphash_k0, 0);
+ BOOST_CHECK_EQUAL(params.m_siphash_k1, 0);
+ BOOST_CHECK_EQUAL(params.m_P, 0);
+ BOOST_CHECK_EQUAL(params.m_M, 1);
+}
+
BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
{
CScript included_scripts[5], excluded_scripts[3];
@@ -88,6 +101,17 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
for (const CScript& script : excluded_scripts) {
BOOST_CHECK(!filter.Match(GCSFilter::Element(script.begin(), script.end())));
}
+
+ // Test serialization/unserialization.
+ BlockFilter block_filter2;
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << block_filter;
+ stream >> block_filter2;
+
+ BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType());
+ BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash());
+ BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter());
}
BOOST_AUTO_TEST_CASE(blockfilters_json_test)
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 57e4b067c0..8da8cfc00c 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -62,7 +62,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Check that both versions serialize back to the public version.
std::string pub1 = parse_priv->ToString();
- std::string pub2 = parse_priv->ToString();
+ std::string pub2 = parse_pub->ToString();
BOOST_CHECK_EQUAL(pub, pub1);
BOOST_CHECK_EQUAL(pub, pub2);
@@ -79,19 +79,42 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0);
BOOST_CHECK_EQUAL(parse_priv->IsRange(), (flags & RANGE) != 0);
-
- // Is not ranged descriptor, only a single result is expected.
+ // * For ranged descriptors, the `scripts` parameter is a list of expected result outputs, for subsequent
+ // positions to evaluate the descriptors on (so the first element of `scripts` is for evaluating the
+ // descriptor at 0; the second at 1; and so on). To verify this, we evaluate the descriptors once for
+ // each element in `scripts`.
+ // * For non-ranged descriptors, we evaluate the descriptors at positions 0, 1, and 2, but expect the
+ // same result in each case, namely the first element of `scripts`. Because of that, the size of
+ // `scripts` must be one in that case.
if (!(flags & RANGE)) assert(scripts.size() == 1);
-
size_t max = (flags & RANGE) ? scripts.size() : 3;
+
+ // Iterate over the position we'll evaluate the descriptors in.
for (size_t i = 0; i < max; ++i) {
+ // Call the expected result scripts `ref`.
const auto& ref = scripts[(flags & RANGE) ? i : 0];
+ // When t=0, evaluate the `prv` descriptor; when t=1, evaluate the `pub` descriptor.
for (int t = 0; t < 2; ++t) {
+ // When the descriptor is hardened, evaluate with access to the private keys inside.
const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
- FlatSigningProvider script_provider;
- std::vector<CScript> spks;
- BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider));
+
+ // Evaluate the descriptor selected by `t` in poisition `i`.
+ FlatSigningProvider script_provider, script_provider_cached;
+ std::vector<CScript> spks, spks_cached;
+ std::vector<unsigned char> cache;
+ BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider, &cache));
+
+ // Compare the output with the expected result.
BOOST_CHECK_EQUAL(spks.size(), ref.size());
+
+ // Try to expand again using cached data, and compare.
+ BOOST_CHECK(parse_pub->ExpandFromCache(i, cache, spks_cached, script_provider_cached));
+ BOOST_CHECK(spks == spks_cached);
+ BOOST_CHECK(script_provider.pubkeys == script_provider_cached.pubkeys);
+ BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);
+ BOOST_CHECK(script_provider.origins == script_provider_cached.origins);
+
+ // For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.
for (size_t n = 0; n < spks.size(); ++n) {
BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n].begin(), spks[n].end()));
BOOST_CHECK_EQUAL(IsSolvable(Merge(key_provider, script_provider), spks[n]), (flags & UNSOLVABLE) == 0);
@@ -102,7 +125,19 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
spend.vout.resize(1);
BOOST_CHECK_MESSAGE(SignSignature(Merge(keys_priv, script_provider), spks[n], spend, 0, 1, SIGHASH_ALL), prv);
}
+
+ /* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */
+ auto inferred = InferDescriptor(spks[n], script_provider);
+ BOOST_CHECK_EQUAL(inferred->IsSolvable(), !(flags & UNSOLVABLE));
+ std::vector<CScript> spks_inferred;
+ FlatSigningProvider provider_inferred;
+ BOOST_CHECK(inferred->Expand(0, provider_inferred, spks_inferred, provider_inferred));
+ BOOST_CHECK_EQUAL(spks_inferred.size(), 1);
+ BOOST_CHECK(spks_inferred[0] == spks[n]);
+ BOOST_CHECK_EQUAL(IsSolvable(provider_inferred, spks_inferred[0]), !(flags & UNSOLVABLE));
+ BOOST_CHECK(provider_inferred.origins == script_provider.origins);
}
+
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
// and then remove it from that set.
for (const auto& origin : script_provider.origins) {
@@ -111,6 +146,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
}
}
}
+
// Verify no expected paths remain that were not observed.
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index 5db62f4bba..bf295042de 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -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/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 12ec00ce02..100d65b779 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -7,7 +7,6 @@
#include <test/test_bitcoin.h>
-#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>
@@ -21,7 +20,7 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt
}
boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min();
if (rescheduleTime != noTime) {
- CScheduler::Function f = boost::bind(&microTask, boost::ref(s), boost::ref(mutex), boost::ref(counter), -delta + 1, noTime);
+ CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
s.schedule(f, rescheduleTime);
}
}
@@ -69,8 +68,8 @@ BOOST_AUTO_TEST_CASE(manythreads)
boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng));
boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
- CScheduler::Function f = boost::bind(&microTask, boost::ref(microTasks),
- boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]),
+ CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
+ std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
randomDelta(rng), tReschedule);
microTasks.schedule(f, t);
}
@@ -82,20 +81,20 @@ BOOST_AUTO_TEST_CASE(manythreads)
// As soon as these are created they will start running and servicing the queue
boost::thread_group microThreads;
for (int i = 0; i < 5; i++)
- microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, &microTasks));
+ microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
MicroSleep(600);
now = boost::chrono::system_clock::now();
// More threads and more tasks:
for (int i = 0; i < 5; i++)
- microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, &microTasks));
+ microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
for (int i = 0; i < 100; i++) {
boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng));
boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
- CScheduler::Function f = boost::bind(&microTask, boost::ref(microTasks),
- boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]),
+ CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
+ std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
randomDelta(rng), tReschedule);
microTasks.schedule(f, t);
}
@@ -126,7 +125,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
// if they don't we'll get out of order behaviour
boost::thread_group threads;
for (int i = 0; i < 5; ++i) {
- threads.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler));
+ threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
}
// these are not atomic, if SinglethreadedSchedulerClient prevents
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 5b10935302..3a2a11ef98 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));
}
}
@@ -213,14 +213,22 @@ BOOST_AUTO_TEST_CASE(is)
BOOST_CHECK(p2sh.IsPayToScriptHash());
// Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes:
- static const unsigned char direct[] = { OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
- BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToScriptHash());
- static const unsigned char pushdata1[] = { OP_HASH160, OP_PUSHDATA1, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
- BOOST_CHECK(!CScript(pushdata1, pushdata1+sizeof(pushdata1)).IsPayToScriptHash());
- static const unsigned char pushdata2[] = { OP_HASH160, OP_PUSHDATA2, 20,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
- BOOST_CHECK(!CScript(pushdata2, pushdata2+sizeof(pushdata2)).IsPayToScriptHash());
- static const unsigned char pushdata4[] = { OP_HASH160, OP_PUSHDATA4, 20,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
- BOOST_CHECK(!CScript(pushdata4, pushdata4+sizeof(pushdata4)).IsPayToScriptHash());
+ std::vector<unsigned char> direct = {OP_HASH160, 20};
+ direct.insert(direct.end(), 20, 0);
+ direct.push_back(OP_EQUAL);
+ BOOST_CHECK(CScript(direct.begin(), direct.end()).IsPayToScriptHash());
+ std::vector<unsigned char> pushdata1 = {OP_HASH160, OP_PUSHDATA1, 20};
+ pushdata1.insert(pushdata1.end(), 20, 0);
+ pushdata1.push_back(OP_EQUAL);
+ BOOST_CHECK(!CScript(pushdata1.begin(), pushdata1.end()).IsPayToScriptHash());
+ std::vector<unsigned char> pushdata2 = {OP_HASH160, 20, 0};
+ pushdata2.insert(pushdata2.end(), 20, 0);
+ pushdata2.push_back(OP_EQUAL);
+ BOOST_CHECK(!CScript(pushdata2.begin(), pushdata2.end()).IsPayToScriptHash());
+ std::vector<unsigned char> pushdata4 = {OP_HASH160, 20, 0, 0, 0};
+ pushdata4.insert(pushdata4.end(), 20, 0);
+ pushdata4.push_back(OP_EQUAL);
+ BOOST_CHECK(!CScript(pushdata4.begin(), pushdata4.end()).IsPayToScriptHash());
CScript not_p2sh;
BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
@@ -266,7 +274,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 +284,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 +301,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 +310,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 +336,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 +358,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 +370,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..858bb512fc 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();
@@ -92,7 +89,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
- threadGroup.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler));
+ threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
mempool.setSanityCheck(1.0);
@@ -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..39cff3f463 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)
@@ -414,14 +414,15 @@ static void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
script = PushAll(stack);
}
-BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
+BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
+{
CMutableTransaction mtx;
mtx.nVersion = 1;
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());
@@ -456,9 +457,8 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
}
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
- auto vstream = WithOrVersion(&ssout, 0);
- vstream << mtx;
- CTransaction tx(deserialize, vstream);
+ ssout << mtx;
+ CTransaction tx(deserialize, ssout);
// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
@@ -467,7 +467,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
for (int i=0; i<20; i++)
- threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue)));
+ threadGroup.create_thread(std::bind(&CCheckQueue<CScriptCheck>::Thread, std::ref(scriptcheckqueue)));
std::vector<Coin> coins;
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
@@ -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/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 473ec5addf..c2777cd6d1 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Bitcoin Core developers
+// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
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..44432cd0a1 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -54,7 +54,7 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash)
CScript pubKey;
pubKey << i++ << OP_TRUE;
- auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey, false);
+ auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
@@ -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/threadinterrupt.h b/src/threadinterrupt.h
index 9c6fccfcde..2743571379 100644
--- a/src/threadinterrupt.h
+++ b/src/threadinterrupt.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2017 The Bitcoin Core developers
+// Copyright (c) 2016-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 229cc7d553..c9ee6f9f81 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -15,7 +15,6 @@
#include <set>
#include <stdlib.h>
-#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
@@ -459,8 +458,8 @@ TorController::TorController(struct event_base* _base, const std::string& _targe
if (!reconnect_ev)
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
// Start connection attempts immediately
- if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1),
- boost::bind(&TorController::disconnected_cb, this, _1) )) {
+ if (!conn.Connect(_target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target);
}
// Read service private key if cached
@@ -538,7 +537,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
// Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient
// choice. TODO; refactor the shutdown sequence some day.
_conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()),
- boost::bind(&TorController::add_onion_cb, this, _1, _2));
+ std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Authentication failed\n");
}
@@ -597,7 +596,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr
}
std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce);
- _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
}
@@ -646,23 +645,23 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
if (methods.count("HASHEDPASSWORD")) {
LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n");
boost::replace_all(torpassword, "\"", "\\\"");
- _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
}
} else if (methods.count("NULL")) {
LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
- _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
- // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2));
+ // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
GetRandBytes(clientNonce.data(), TOR_NONCE_SIZE);
- _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2));
+ _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), std::bind(&TorController::authchallenge_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
if (status_cookie.first) {
LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE);
@@ -684,7 +683,7 @@ void TorController::connected_cb(TorControlConnection& _conn)
{
reconnect_timeout = RECONNECT_TIMEOUT_START;
// First send a PROTOCOLINFO command to figure out what authentication is expected
- if (!_conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2)))
+ if (!_conn.Command("PROTOCOLINFO 1", std::bind(&TorController::protocolinfo_cb, this, std::placeholders::_1, std::placeholders::_2)))
LogPrintf("tor: Error sending initial protocolinfo command\n");
}
@@ -711,8 +710,8 @@ void TorController::Reconnect()
/* Try to reconnect and reestablish if we get booted - for example, Tor
* may be restarting.
*/
- if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1),
- boost::bind(&TorController::disconnected_cb, this, _1) )) {
+ if (!conn.Connect(target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target);
}
}
diff --git a/src/uint256.h b/src/uint256.h
index 26a3331d92..97e0cfa015 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -12,7 +12,6 @@
#include <stdint.h>
#include <string>
#include <vector>
-#include <crypto/common.h>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
@@ -123,16 +122,6 @@ class uint256 : public base_blob<256> {
public:
uint256() {}
explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
-
- /** A cheap hash function that just returns 64 bits from the result, it can be
- * used when the contents are considered uniformly random. It is not appropriate
- * when the value can easily be influenced from outside as e.g. a network adversary could
- * provide values to trigger worst-case behavior.
- */
- uint64_t GetCheapHash() const
- {
- return ReadLE64(data);
- }
};
/* uint256 from const char *.
diff --git a/src/validation.cpp b/src/validation.cpp
index 241957878e..a18e449af6 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1680,8 +1680,7 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
-// Protected by cs_main
-VersionBitsCache versionbitscache;
+VersionBitsCache versionbitscache GUARDED_BY(cs_main);
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
@@ -1722,8 +1721,7 @@ public:
}
};
-// Protected by cs_main
-static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
// mainnet. We no longer need to support disabling the segwit deployment
@@ -2368,14 +2366,11 @@ class ConnectTrace {
private:
std::vector<PerBlockConnectTrace> blocksConnected;
CTxMemPool &pool;
+ boost::signals2::scoped_connection m_connNotifyEntryRemoved;
public:
explicit ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
- pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
- }
-
- ~ConnectTrace() {
- pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ m_connNotifyEntryRemoved = pool.NotifyEntryRemoved.connect(std::bind(&ConnectTrace::NotifyEntryRemoved, this, std::placeholders::_1, std::placeholders::_2));
}
void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
@@ -2489,7 +2484,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
- assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
+ assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
@@ -2779,7 +2774,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
// call preciousblock 2**31-1 times on the same set of tips...
nBlockReverseSequenceId--;
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
}
@@ -2840,7 +2835,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
it++;
@@ -2870,7 +2865,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
@@ -2948,7 +2943,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);
@@ -3269,6 +3264,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;
}
@@ -3375,10 +3371,30 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
- // If the previous block index isn't valid, determine if it descends from any block which
- // has been found invalid (m_failed_blocks), then mark pindexPrev and any blocks
- // between them as failed.
+ /* Determine if this block descends from any block which has been found
+ * invalid (m_failed_blocks), then mark pindexPrev and any blocks between
+ * them as failed. For example:
+ *
+ * D3
+ * /
+ * B2 - C2
+ * / \
+ * A D2 - E2 - F2
+ * \
+ * B1 - C1 - D1 - E1
+ *
+ * In the case that we attempted to reorg from E1 to F2, only to find
+ * C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
+ * but NOT D3 (it was not in any of our candidate sets at the time).
+ *
+ * In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
+ * in LoadBlockIndex.
+ */
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
+ // The above does not mean "invalid": it checks if the previous block
+ // hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance
+ // optimization, in the common case of adding a new block to the tip,
+ // we don't need to iterate over the failed blocks list.
for (const CBlockIndex* failedit : m_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus & BLOCK_FAILED_VALID);
@@ -3532,12 +3548,14 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
CBlockIndex *pindex = nullptr;
if (fNewBlock) *fNewBlock = false;
CValidationState state;
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
- bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
+ // Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
+ // Ensure that CheckBlock() passes before calling AcceptBlock, as
+ // belt-and-suspenders.
+ bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
@@ -3819,8 +3837,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 +3855,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 +3869,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;
@@ -3982,7 +3998,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
LogPrintf("[0%%]..."); /* Continued */
for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
boost::this_thread::interruption_point();
- int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
@@ -4040,7 +4056,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (nCheckLevel >= 4) {
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
- uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false);
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ if (reportDone < percentageDone/10) {
+ // report every 10% step
+ LogPrintf("[%d%%]...", percentageDone); /* Continued */
+ reportDone = percentageDone/10;
+ }
+ uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
pindex = chainActive.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
@@ -4222,7 +4244,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 +4545,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 +4558,9 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
}
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
- // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set.
- assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned).
- assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0));
+ // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
+ assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
+ assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
diff --git a/src/validation.h b/src/validation.h
index 3e98ebc866..b5548a9293 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -12,6 +12,7 @@
#include <amount.h>
#include <coins.h>
+#include <crypto/common.h> // for ReadLE64
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <policy/feerate.h>
@@ -138,7 +139,10 @@ static const int DEFAULT_STOPATHEIGHT = 0;
struct BlockHasher
{
- size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
+ // this used to call `GetCheapHash()` in uint256, which was later moved; the
+ // cheap hash function simply calls ReadLE64() however, so the end result is
+ // identical
+ size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
extern CScript COINBASE_FLAGS;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 214a9ffba9..533d412888 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -17,6 +17,18 @@
#include <boost/signals2/signal.hpp>
+struct ValidationInterfaceConnections {
+ boost::signals2::scoped_connection UpdatedBlockTip;
+ boost::signals2::scoped_connection TransactionAddedToMempool;
+ boost::signals2::scoped_connection BlockConnected;
+ boost::signals2::scoped_connection BlockDisconnected;
+ boost::signals2::scoped_connection TransactionRemovedFromMempool;
+ boost::signals2::scoped_connection ChainStateFlushed;
+ boost::signals2::scoped_connection Broadcast;
+ boost::signals2::scoped_connection BlockChecked;
+ boost::signals2::scoped_connection NewPoWValidBlock;
+};
+
struct MainSignalsInstance {
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
@@ -32,12 +44,18 @@ struct MainSignalsInstance {
// but must ensure all callbacks happen in-order, so we end up creating
// our own queue here :(
SingleThreadedSchedulerClient m_schedulerClient;
+ std::unordered_map<CValidationInterface*, ValidationInterfaceConnections> m_connMainSignals;
explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
};
static CMainSignals g_signals;
+// This map has to a separate global instead of a member of MainSignalsInstance,
+// because RegisterWithMempoolSignals is currently called before RegisterBackgroundSignalScheduler,
+// so MainSignalsInstance hasn't been created yet.
+static std::unordered_map<CTxMemPool*, boost::signals2::scoped_connection> g_connNotifyEntryRemoved;
+
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
assert(!m_internals);
m_internals.reset(new MainSignalsInstance(&scheduler));
@@ -59,11 +77,11 @@ size_t CMainSignals::CallbacksPending() {
}
void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) {
- pool.NotifyEntryRemoved.connect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2));
+ g_connNotifyEntryRemoved.emplace(&pool, pool.NotifyEntryRemoved.connect(std::bind(&CMainSignals::MempoolEntryRemoved, this, std::placeholders::_1, std::placeholders::_2)));
}
void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) {
- pool.NotifyEntryRemoved.disconnect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2));
+ g_connNotifyEntryRemoved.erase(&pool);
}
CMainSignals& GetMainSignals()
@@ -72,42 +90,27 @@ CMainSignals& GetMainSignals()
}
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.m_internals->UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
- g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
- g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
- g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
- g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
- g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
- g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
+ ValidationInterfaceConnections& conns = g_signals.m_internals->m_connMainSignals[pwalletIn];
+ conns.UpdatedBlockTip = g_signals.m_internals->UpdatedBlockTip.connect(std::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ conns.TransactionAddedToMempool = g_signals.m_internals->TransactionAddedToMempool.connect(std::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1));
+ conns.BlockConnected = g_signals.m_internals->BlockConnected.connect(std::bind(&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1));
+ conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
+ conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
+ conns.Broadcast = g_signals.m_internals->Broadcast.connect(std::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, std::placeholders::_1, std::placeholders::_2));
+ conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
+ conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
- g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
- g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
- g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
- g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
- g_signals.m_internals->TransactionRemovedFromMempool.disconnect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
- g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
+ g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
}
void UnregisterAllValidationInterfaces() {
if (!g_signals.m_internals) {
return;
}
- g_signals.m_internals->BlockChecked.disconnect_all_slots();
- g_signals.m_internals->Broadcast.disconnect_all_slots();
- g_signals.m_internals->ChainStateFlushed.disconnect_all_slots();
- g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
- g_signals.m_internals->BlockConnected.disconnect_all_slots();
- g_signals.m_internals->BlockDisconnected.disconnect_all_slots();
- g_signals.m_internals->TransactionRemovedFromMempool.disconnect_all_slots();
- g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots();
- g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots();
+ g_signals.m_internals->m_connMainSignals.clear();
}
void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) {
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..ed1e2d3940 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"
@@ -860,9 +836,98 @@ UniValue dumpwallet(const JSONRPCRequest& request)
return reply;
}
+struct ImportData
+{
+ // Input data
+ std::unique_ptr<CScript> redeemscript; //!< Provided redeemScript; will be moved to `import_scripts` if relevant.
+ std::unique_ptr<CScript> witnessscript; //!< Provided witnessScript; will be moved to `import_scripts` if relevant.
+
+ // Output data
+ std::set<CScript> import_scripts;
+ std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
+};
+
+enum class ScriptContext
+{
+ TOP, //! Top-level scriptPubKey
+ P2SH, //! P2SH redeemScript
+ WITNESS_V0, //! P2WSH witnessScript
+};
+
+// Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used.
+// Returns an error string, or the empty string for success.
+static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx)
+{
+ // Use Solver to obtain script type and parsed pubkeys or hashes:
+ std::vector<std::vector<unsigned char>> solverdata;
+ txnouttype script_type = Solver(script, solverdata);
+
+ switch (script_type) {
+ case TX_PUBKEY: {
+ CPubKey pubkey(solverdata[0].begin(), solverdata[0].end());
+ import_data.used_keys.emplace(pubkey.GetID(), false);
+ return "";
+ }
+ case TX_PUBKEYHASH: {
+ CKeyID id = CKeyID(uint160(solverdata[0]));
+ import_data.used_keys[id] = true;
+ return "";
+ }
+ case TX_SCRIPTHASH: {
+ if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
+ if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
+ assert(script_ctx == ScriptContext::TOP);
+ CScriptID id = CScriptID(uint160(solverdata[0]));
+ auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
+ if (!subscript) return "missing redeemscript";
+ if (CScriptID(*subscript) != id) return "redeemScript does not match the scriptPubKey";
+ import_data.import_scripts.emplace(*subscript);
+ return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
+ }
+ case TX_MULTISIG: {
+ for (size_t i = 1; i + 1< solverdata.size(); ++i) {
+ CPubKey pubkey(solverdata[i].begin(), solverdata[i].end());
+ import_data.used_keys.emplace(pubkey.GetID(), false);
+ }
+ return "";
+ }
+ case TX_WITNESS_V0_SCRIPTHASH: {
+ if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
+ uint256 fullid(solverdata[0]);
+ CScriptID id;
+ CRIPEMD160().Write(fullid.begin(), fullid.size()).Finalize(id.begin());
+ auto subscript = std::move(import_data.witnessscript); // Remove redeemscript from import_data to check for superfluous script later.
+ if (!subscript) return "missing witnessscript";
+ if (CScriptID(*subscript) != id) return "witnessScript does not match the scriptPubKey or redeemScript";
+ if (script_ctx == ScriptContext::TOP) {
+ import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WSH requires the TOP script imported (see script/ismine.cpp)
+ }
+ import_data.import_scripts.emplace(*subscript);
+ return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
+ }
+ case TX_WITNESS_V0_KEYHASH: {
+ if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
+ CKeyID id = CKeyID(uint160(solverdata[0]));
+ import_data.used_keys[id] = true;
+ if (script_ctx == ScriptContext::TOP) {
+ import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WPKH requires the TOP script imported (see script/ismine.cpp)
+ }
+ return "";
+ }
+ case TX_NULL_DATA:
+ return "unspendable script";
+ case TX_NONSTANDARD:
+ case TX_WITNESS_UNKNOWN:
+ default:
+ return "unrecognized script";
+ }
+}
static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
+ UniValue warnings(UniValue::VARR);
+ UniValue result(UniValue::VOBJ);
+
try {
// First ensure scriptPubKey has either a script or JSON with "address" string
const UniValue& scriptPubKey = data["scriptPubKey"];
@@ -884,18 +949,16 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// Generate the script and destination for the scriptPubKey provided
CScript script;
CTxDestination dest;
-
if (!isScript) {
dest = DecodeDestination(output);
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
}
script = GetScriptForDestination(dest);
} else {
if (!IsHex(output)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
}
-
std::vector<unsigned char> vData(ParseHex(output));
script = CScript(vData.begin(), vData.end());
if (!ExtractDestination(script, dest) && !internal) {
@@ -903,203 +966,161 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
}
}
- // Watchonly and private keys
- if (watchOnly && keys.size()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Watch-only addresses should not include private keys");
- }
-
- // Internal addresses should not have a label
- if (internal && data.exists("label")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
- }
-
- // Force users to provide the witness script in its field rather than redeemscript
- if (!strRedeemScript.empty() && script.IsPayToWitnessScriptHash()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WSH addresses have an empty redeemscript. Please provide the witnessscript instead.");
- }
-
- CScript scriptpubkey_script = script;
- CTxDestination scriptpubkey_dest = dest;
- bool allow_p2wpkh = true;
-
- // P2SH
- if (!strRedeemScript.empty() && script.IsPayToScriptHash()) {
- // Check the redeemScript is valid
+ // Parse all arguments
+ ImportData import_data;
+ if (strRedeemScript.size()) {
if (!IsHex(strRedeemScript)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script: must be hex string");
- }
-
- // Import redeem script.
- std::vector<unsigned char> vData(ParseHex(strRedeemScript));
- CScript redeemScript = CScript(vData.begin(), vData.end());
- CScriptID redeem_id(redeemScript);
-
- // Check that the redeemScript and scriptPubKey match
- if (GetScriptForDestination(redeem_id) != script) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The redeemScript does not match the scriptPubKey");
- }
-
- pwallet->MarkDirty();
-
- if (!pwallet->AddWatchOnly(redeemScript, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
-
- if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
}
-
- // Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below
- script = redeemScript;
- ExtractDestination(script, dest);
+ auto parsed_redeemscript = ParseHex(strRedeemScript);
+ import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
}
-
- // (P2SH-)P2WSH
- if (!witness_script_hex.empty() && script.IsPayToWitnessScriptHash()) {
+ if (witness_script_hex.size()) {
if (!IsHex(witness_script_hex)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script: must be hex string");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
}
-
- // Generate the scripts
- std::vector<unsigned char> witness_script_parsed(ParseHex(witness_script_hex));
- CScript witness_script = CScript(witness_script_parsed.begin(), witness_script_parsed.end());
- CScriptID witness_id(witness_script);
-
- // Check that the witnessScript and scriptPubKey match
- if (GetScriptForDestination(WitnessV0ScriptHash(witness_script)) != script) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The witnessScript does not match the scriptPubKey or redeemScript");
+ auto parsed_witnessscript = ParseHex(witness_script_hex);
+ import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ }
+ std::map<CKeyID, CPubKey> pubkey_map;
+ for (size_t i = 0; i < pubKeys.size(); ++i) {
+ const auto& str = pubKeys[i].get_str();
+ if (!IsHex(str)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
}
-
- // Add the witness script as watch only only if it is not for P2SH-P2WSH
- if (!scriptpubkey_script.IsPayToScriptHash() && !pwallet->AddWatchOnly(witness_script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ auto parsed_pubkey = ParseHex(str);
+ CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
}
-
- if (!pwallet->HaveCScript(witness_id) && !pwallet->AddCScript(witness_script)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2wsh witnessScript to wallet");
+ pubkey_map.emplace(pubkey.GetID(), pubkey);
+ }
+ std::map<CKeyID, CKey> privkey_map;
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const auto& str = keys[i].get_str();
+ CKey key = DecodeSecret(str);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+ CPubKey pubkey = key.GetPubKey();
+ CKeyID id = pubkey.GetID();
+ if (pubkey_map.count(id)) {
+ pubkey_map.erase(id);
}
+ privkey_map.emplace(id, key);
+ }
- // Now set script to the witnessScript so we parse the inner script as P2PK or P2PKH below
- script = witness_script;
- ExtractDestination(script, dest);
- allow_p2wpkh = false; // P2WPKH cannot be embedded in P2WSH
+ // Internal addresses should not have a label
+ if (internal && data.exists("label")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
- // (P2SH-)P2PK/P2PKH/P2WPKH
- if (dest.type() == typeid(CKeyID) || dest.type() == typeid(WitnessV0KeyHash)) {
- if (!allow_p2wpkh && dest.type() == typeid(WitnessV0KeyHash)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WPKH cannot be embedded in P2WSH");
- }
- if (keys.size() > 1 || pubKeys.size() > 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "More than one key given for one single-key address");
+ // Verify and process input data
+ bool have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
+ if (have_solving_data) {
+ // Match up data in import_data with the scriptPubKey in script.
+ auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
+
+ // Verify whether the watchonly option corresponds to the availability of private keys.
+ bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
+ if (!watchOnly && !spendable) {
+ warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
}
- CPubKey pubkey;
- if (keys.size()) {
- pubkey = DecodeSecret(keys[0].get_str()).GetPubKey();
+ if (watchOnly && spendable) {
+ warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
}
- if (pubKeys.size()) {
- const std::string& strPubKey = pubKeys[0].get_str();
- if (!IsHex(strPubKey)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
- }
- std::vector<unsigned char> vData(ParseHex(pubKeys[0].get_str()));
- CPubKey pubkey_temp(vData.begin(), vData.end());
- if (pubkey.size() && pubkey_temp != pubkey) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key does not match public key for address");
- }
- pubkey = pubkey_temp;
- }
- if (pubkey.size() > 0) {
- if (!pubkey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
- }
- // Check the key corresponds to the destination given
- std::vector<CTxDestination> destinations = GetAllDestinationsForKey(pubkey);
- if (std::find(destinations.begin(), destinations.end(), dest) == destinations.end()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Key does not match address destination");
+ // Check that all required keys for solvability are provided.
+ if (error.empty()) {
+ for (const auto& require_key : import_data.used_keys) {
+ if (!require_key.second) continue; // Not a required key
+ if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
+ error = "some required keys are missing";
+ }
}
+ }
- // This is necessary to force the wallet to import the pubKey
- CScript scriptRawPubKey = GetScriptForRawPubKey(pubkey);
-
- if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ if (!error.empty()) {
+ warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
+ import_data = ImportData();
+ pubkey_map.clear();
+ privkey_map.clear();
+ have_solving_data = false;
+ } else {
+ // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
+ if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
+ if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
+ for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
+ auto oldit = it++;
+ if (import_data.used_keys.count(oldit->first) == 0) {
+ warnings.push_back("Ignoring irrelevant private key.");
+ privkey_map.erase(oldit);
+ }
}
-
- pwallet->MarkDirty();
-
- if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
+ auto oldit = it++;
+ auto key_data_it = import_data.used_keys.find(oldit->first);
+ if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
+ warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
+ pubkey_map.erase(oldit);
+ }
}
}
}
- // Import the address
- if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) {
+ // Check whether we have any work to do
+ if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
}
+ // All good, time to import
pwallet->MarkDirty();
-
- if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
-
- if (!watchOnly && !pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && !pwallet->AddCScript(scriptpubkey_script)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet");
- }
-
- // if not internal add to address book or update label
- if (!internal) {
- assert(IsValidDestination(scriptpubkey_dest));
- pwallet->SetAddressBook(scriptpubkey_dest, label, "receive");
- }
-
- // Import private keys.
- for (size_t i = 0; i < keys.size(); i++) {
- const std::string& strPrivkey = keys[i].get_str();
-
- // Checks.
- CKey key = DecodeSecret(strPrivkey);
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ for (const auto& entry : import_data.import_scripts) {
+ if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
-
- CPubKey pubKey = key.GetPubKey();
- assert(key.VerifyPubKey(pubKey));
-
- CKeyID vchAddress = pubKey.GetID();
- pwallet->MarkDirty();
-
- if (pwallet->HaveKey(vchAddress)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
- }
-
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
-
- if (!pwallet->AddKeyPubKey(key, pubKey)) {
+ }
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
-
pwallet->UpdateTimeFirstKey(timestamp);
}
+ for (const auto& entry : pubkey_map) {
+ const CPubKey& pubkey = entry.second;
+ const CKeyID& id = entry.first;
+ CPubKey temp;
+ if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+ }
+ if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
+ if (!pwallet->AddWatchOnly(script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+ }
+ if (!internal) {
+ assert(IsValidDestination(dest));
+ pwallet->SetAddressBook(dest, label, "receive");
+ }
- UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(true));
- return result;
} catch (const UniValue& e) {
- UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
result.pushKV("error", e);
- return result;
} catch (...) {
- UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
+
result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
- return result;
}
+ if (warnings.size()) result.pushKV("warnings", warnings);
+ return result;
}
static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
@@ -1127,57 +1148,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 to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
+ {
+ {"pubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ }
+ },
+ {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys to import. The 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 (also known as change)"},
+ {"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watched even when not all private keys are provided."},
+ {"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" +
@@ -1186,7 +1199,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
"\nResponse is an array with the same size as the input that has the execution result :\n"
- " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
+ " [{\"success\": true}, {\"success\": true, \"warnings\": [\"Ignoring irrelevant private key\"]}, {\"success\": false, \"error\": {\"code\": -1, \"message\": \"Internal Server Error\"}}, ...]\n");
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ecc8fa2643..c1cdd0b2ee 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -22,6 +22,7 @@
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <script/sign.h>
#include <shutdown.h>
#include <timedata.h>
@@ -154,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"
@@ -218,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"
@@ -275,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\"")
@@ -358,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"
@@ -521,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"
@@ -592,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"
@@ -666,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"
@@ -740,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"
@@ -823,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,
+ {"dummy", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Must be set to \"\" for backwards compatibility.", "\"\""},
+ {"amounts", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object with addresses and amounts",
{
- {"address", RPCArg::Type::AMOUNT, false},
+ {"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"},
},
- false},
- {"minconf", RPCArg::Type::NUM, true},
- {"comment", RPCArg::Type::STR, true},
- {"subtractfeefrom", RPCArg::Type::ARR,
- {
- {"address", RPCArg::Type::STR, true},
- },
- 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"
@@ -994,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"
@@ -1237,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"
@@ -1295,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"
@@ -1441,25 +1386,25 @@ 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"
" \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
- " \"category\":\"send|receive\", (string) The transaction category.\n"
+ " \"category\": (string) The transaction category.\n"
+ " \"send\" Transactions sent.\n"
+ " \"receive\" Non-coinbase transactions received.\n"
+ " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ " \"orphan\" Orphaned coinbase transactions received.\n"
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- " for the 'receive' category,\n"
+ " for all other categories\n"
" \"label\": \"label\", (string) A comment for the address/transaction, if any\n"
" \"vout\": n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
@@ -1576,35 +1521,35 @@ 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"
- " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
- " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
- " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n"
- " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n"
+ " \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
+ " \"category\": (string) The transaction category.\n"
+ " \"send\" Transactions sent.\n"
+ " \"receive\" Non-coinbase transactions received.\n"
+ " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ " \"orphan\" Orphaned coinbase transactions received.\n"
+ " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ " for all other categories\n"
" \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n"
- " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
+ " \"confirmations\": n, (numeric) The number of confirmations for the transaction.\n"
" When it's < 0, it means the transaction conflicted that many blocks ago.\n"
- " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
- " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive' category of transactions.\n"
+ " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n"
+ " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n"
" \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
- " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
+ " \"txid\": \"transactionid\", (string) The transaction id.\n"
" \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n"
- " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
+ " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT).\n"
" \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
" may be unknown for unconfirmed transactions not in the mempool\n"
" \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n"
@@ -1721,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"
@@ -1745,7 +1687,12 @@ static UniValue gettransaction(const JSONRPCRequest& request)
" \"details\" : [\n"
" {\n"
" \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
- " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
+ " \"category\" : (string) The transaction category.\n"
+ " \"send\" Transactions sent.\n"
+ " \"receive\" Non-coinbase transactions received.\n"
+ " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ " \"orphan\" Orphaned coinbase transactions received.\n"
" \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
" \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
" \"vout\" : n, (numeric) the vout value\n"
@@ -1825,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\"")
@@ -1871,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\"")
@@ -1909,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", "")
@@ -1963,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"
@@ -2009,21 +1947,13 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
nSleepTime = MAX_SLEEP_TIME;
}
- if (strWalletPass.length() > 0)
- {
- if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
- }
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
+
+ if (!pwallet->Unlock(strWalletPass)) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
}
- else
- throw std::runtime_error(
- RPCHelpMan{"walletpassphrase",
- "Stores the wallet decryption key in memory for <timeout> seconds.",
- {
- {"passphrase", RPCArg::Type::STR, false},
- {"timeout", RPCArg::Type::NUM, false},
- }}
- .ToString());
pwallet->TopUpKeyPool();
@@ -2059,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\"")
@@ -2089,15 +2016,9 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
strNewWalletPass.reserve(100);
strNewWalletPass = request.params[1].get_str().c_str();
- if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
- throw std::runtime_error(
- RPCHelpMan{"walletpassphrasechange",
- "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.",
- {
- {"oldpassphrase", RPCArg::Type::STR, false},
- {"newpassphrase", RPCArg::Type::STR, false},
- }}
- .ToString());
+ if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
@@ -2168,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\"") +
@@ -2200,14 +2119,9 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
strWalletPass.reserve(100);
strWalletPass = request.params[0].get_str().c_str();
- if (strWalletPass.length() < 1)
- throw std::runtime_error(
- RPCHelpMan{"encryptwallet",
- "Encrypts the wallet with <passphrase>.",
- {
- {"passphrase", RPCArg::Type::STR, false},
- }}
- .ToString());
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->EncryptWallet(strWalletPass)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
@@ -2236,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"
@@ -2426,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"
@@ -2602,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"
@@ -2657,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"
@@ -2715,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")
@@ -2816,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"
@@ -2864,6 +2742,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
+ " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
" \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
" and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
@@ -2982,6 +2861,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
+ if (out.fSolvable) {
+ auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
+ entry.pushKV("desc", descriptor->ToString());
+ }
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
@@ -3128,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"
@@ -3231,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"
@@ -3333,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"
@@ -3487,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"
@@ -3544,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"
@@ -3605,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);
@@ -3756,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"
@@ -3768,6 +3599,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"solvable\" : true|false, (boolean) If the address is solvable by the wallet\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
+ " \"solvable\" : true|false, (boolean) Whether we know how to spend coins sent to this address, ignoring the possible lack of private keys\n"
+ " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable)\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
" \"ischange\" : true|false, (boolean) If the address was used for change output\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
@@ -3821,6 +3654,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
+ bool solvable = IsSolvable(*pwallet, scriptPubKey);
+ ret.pushKV("solvable", solvable);
+ if (solvable) {
+ ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString());
+ }
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
ret.pushKV("solvable", IsSolvable(*pwallet, scriptPubKey));
UniValue detail = DescribeWalletAddress(pwallet, dest);
@@ -3879,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"
@@ -3928,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"
@@ -3986,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")
@@ -4129,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"
@@ -4203,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 c6aac8aad5..1ed1926af2 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -17,6 +17,7 @@
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
+#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
@@ -33,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()));
@@ -46,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.
{
@@ -53,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);
}
@@ -68,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.
@@ -286,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()
@@ -394,4 +452,47 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
}
+// Explicit calculation which is used to test the wallet constant
+// We get the same virtual size due to rounding(weight/4) for both use_max_sig values
+static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
+{
+ // Generate ephemeral valid pubkey
+ CKey key;
+ key.MakeNewKey(true);
+ CPubKey pubkey = key.GetPubKey();
+
+ // Generate pubkey hash
+ uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));
+
+ // Create inner-script to enter into keystore. Key hash can't be 0...
+ CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
+
+ // Create outer P2SH script for the output
+ uint160 script_id(Hash160(inner_script.begin(), inner_script.end()));
+ CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
+
+ // Add inner-script to key store and key to watchonly
+ CBasicKeyStore keystore;
+ keystore.AddCScript(inner_script);
+ keystore.AddKeyPubKey(key, pubkey);
+
+ // Fill in dummy signatures for fee calculation.
+ SignatureData sig_data;
+
+ if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
+ // We're hand-feeding it correct arguments; shouldn't happen
+ assert(false);
+ }
+
+ CTxIn tx_in;
+ UpdateInput(tx_in, sig_data);
+ return (size_t)GetVirtualTransactionInputSize(tx_in);
+}
+
+BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
+{
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 360d0f177c..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__);
@@ -1530,8 +1479,6 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionInputSize(txn.vin[0]);
@@ -1613,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;
}
}
@@ -1626,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();
@@ -1647,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);
@@ -1669,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))));
}
@@ -1686,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;
@@ -1709,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()
@@ -2451,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);
@@ -2755,7 +2716,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
- coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
+ }
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
@@ -2904,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.
@@ -3693,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;
@@ -3703,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();
}
}
}
@@ -4164,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 f96798201f..95a2c833f8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -85,6 +85,9 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+//! Pre-calculated constants for input size estimation in *virtual size*
+static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
+
class CBlockIndex;
class CCoinControl;
class COutput;
@@ -896,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);