aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rwxr-xr-xcontrib/devtools/check-doc.py2
-rw-r--r--contrib/gitian-keys/laanwj-key.pgpbin1713 -> 17689 bytes
-rw-r--r--doc/REST-interface.md28
-rw-r--r--src/.clang-format1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/bench/coin_selection.cpp1
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/bloom.cpp1
-rw-r--r--src/chain.cpp19
-rw-r--r--src/chain.h3
-rw-r--r--src/checkpoints.cpp4
-rw-r--r--src/checkqueue.h1
-rw-r--r--src/coins.cpp9
-rw-r--r--src/coins.h16
-rw-r--r--src/core_write.cpp1
-rw-r--r--src/httprpc.cpp58
-rw-r--r--src/httpserver.cpp22
-rw-r--r--src/init.cpp128
-rw-r--r--src/keystore.h24
-rw-r--r--src/memusage.h1
-rw-r--r--src/net.cpp5
-rw-r--r--src/net.h1
-rw-r--r--src/net_processing.cpp24
-rw-r--r--src/policy/fees.cpp14
-rw-r--r--src/policy/fees.h11
-rw-r--r--src/policy/policy.cpp1
-rw-r--r--src/prevector.h2
-rw-r--r--src/qt/addresstablemodel.cpp1
-rw-r--r--src/qt/bitcoin.cpp1
-rw-r--r--src/qt/bitcoinstrings.cpp50
-rw-r--r--src/qt/coincontroldialog.cpp6
-rw-r--r--src/qt/locale/bitcoin_en.ts593
-rw-r--r--src/qt/recentrequeststablemodel.cpp1
-rw-r--r--src/qt/sendcoinsdialog.cpp6
-rw-r--r--src/qt/splashscreen.cpp24
-rw-r--r--src/qt/splashscreen.h8
-rw-r--r--src/qt/test/rpcnestedtests.cpp22
-rw-r--r--src/qt/transactionrecord.cpp1
-rw-r--r--src/qt/transactionview.cpp4
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/reverse_iterator.h39
-rw-r--r--src/rpc/blockchain.cpp2
-rw-r--r--src/rpc/client.cpp7
-rw-r--r--src/rpc/mining.cpp38
-rw-r--r--src/rpc/mining.h15
-rw-r--r--src/rpc/misc.cpp8
-rw-r--r--src/rpc/net.cpp6
-rw-r--r--src/rpc/rawtransaction.cpp9
-rw-r--r--src/rpc/server.cpp1
-rw-r--r--src/scheduler.cpp66
-rw-r--r--src/scheduler.h33
-rw-r--r--src/script/interpreter.h6
-rw-r--r--src/script/ismine.cpp1
-rw-r--r--src/script/sigcache.cpp6
-rw-r--r--src/script/sigcache.h2
-rw-r--r--src/script/sign.cpp3
-rw-r--r--src/script/sign.h8
-rw-r--r--src/script/standard.cpp1
-rw-r--r--src/support/lockedpool.cpp12
-rw-r--r--src/sync.cpp1
-rw-r--r--src/test/addrman_tests.cpp2
-rw-r--r--src/test/allocator_tests.cpp6
-rw-r--r--src/test/getarg_tests.cpp1
-rw-r--r--src/test/multisig_tests.cpp1
-rw-r--r--src/test/net_tests.cpp4
-rw-r--r--src/test/policyestimator_tests.cpp4
-rw-r--r--src/test/prevector_tests.cpp5
-rw-r--r--src/test/script_tests.cpp1
-rw-r--r--src/test/test_bitcoin.cpp9
-rw-r--r--src/test/test_bitcoin.h2
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/txvalidationcache_tests.cpp284
-rw-r--r--src/test/versionbits_tests.cpp10
-rw-r--r--src/timedata.cpp1
-rw-r--r--src/torcontrol.cpp1
-rw-r--r--src/txdb.cpp74
-rw-r--r--src/txdb.h17
-rw-r--r--src/txmempool.cpp3
-rw-r--r--src/txmempool.h14
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/util.cpp4
-rw-r--r--src/validation.cpp328
-rw-r--r--src/validation.h8
-rw-r--r--src/validationinterface.cpp134
-rw-r--r--src/validationinterface.h72
-rw-r--r--src/versionbits.cpp10
-rw-r--r--src/wallet/coincontrol.h4
-rw-r--r--src/wallet/crypter.cpp1
-rw-r--r--src/wallet/crypter.h10
-rw-r--r--src/wallet/db.cpp1
-rw-r--r--src/wallet/feebumper.cpp5
-rw-r--r--src/wallet/feebumper.h3
-rw-r--r--src/wallet/rpcdump.cpp3
-rw-r--r--src/wallet/rpcwallet.cpp155
-rw-r--r--src/wallet/test/accounting_tests.cpp1
-rw-r--r--src/wallet/wallet.cpp25
-rw-r--r--src/wallet/wallet.h10
-rw-r--r--src/wallet/walletdb.cpp1
-rw-r--r--src/zmq/zmqpublishnotifier.h12
-rwxr-xr-xtest/functional/bip9-softforks.py7
-rwxr-xr-xtest/functional/blockchain.py13
-rwxr-xr-xtest/functional/bumpfee.py2
-rwxr-xr-xtest/functional/dbcrash.py281
-rwxr-xr-xtest/functional/fundrawtransaction.py4
-rwxr-xr-xtest/functional/keypool.py2
-rwxr-xr-xtest/functional/listtransactions.py2
-rwxr-xr-xtest/functional/receivedby.py2
-rwxr-xr-xtest/functional/replace-by-fee.py4
-rwxr-xr-xtest/functional/rpcbind_test.py2
-rwxr-xr-xtest/functional/test_framework/test_framework.py201
-rw-r--r--test/functional/test_framework/util.py633
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet-dump.py4
-rwxr-xr-xtest/functional/wallet-encryption.py6
-rwxr-xr-xtest/functional/wallet-hd.py3
-rwxr-xr-xtest/functional/walletbackup.py3
117 files changed, 2614 insertions, 1156 deletions
diff --git a/.travis.yml b/.travis.yml
index fcf4b082f1..a79428fc17 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -68,7 +68,7 @@ script:
- make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false )
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then travis_wait 30 make $MAKEJOBS check VERBOSE=1; fi
- - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude pruning"; fi
+ - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude pruning,dbcrash"; fi
- if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --coverage --quiet ${extended}; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py
index 249214e931..150f368513 100755
--- a/contrib/devtools/check-doc.py
+++ b/contrib/devtools/check-doc.py
@@ -21,7 +21,7 @@ CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' %s" % (CMD_RO
REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"')
REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")')
# list unsupported, deprecated and duplicate args as they need no documentation
-SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize'])
+SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio'])
def main():
used = check_output(CMD_GREP_ARGS, shell=True)
diff --git a/contrib/gitian-keys/laanwj-key.pgp b/contrib/gitian-keys/laanwj-key.pgp
index 559295109d..eed232a872 100644
--- a/contrib/gitian-keys/laanwj-key.pgp
+++ b/contrib/gitian-keys/laanwj-key.pgp
Binary files differ
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index 7fbb174030..caf6782886 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -40,11 +40,13 @@ Only supports JSON as output format.
* headers : (numeric) the current number of headers we have validated
* bestblockhash : (string) the hash of the currently best block
* difficulty : (numeric) the current difficulty
+* mediantime : (numeric) the median time of the 11 blocks before the most recent block on the blockchain
* verificationprogress : (numeric) estimate of verification progress [0..1]
* chainwork : (string) total amount of work in active chain, in hexadecimal
* pruned : (boolean) if the blocks are subject to pruning
* pruneheight : (numeric) heighest block available
* softforks : (array) status of softforks in progress
+* bip9_softforks : (object) status of BIP9 softforks in progress
####Query UTXO set
`GET /rest/getutxos/<checkmempool>/<txid>-<n>/<txid>-<n>/.../<txid>-<n>.<bin|hex|json>`
@@ -57,25 +59,25 @@ Example:
```
$ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff7627ff72e5e8b0f71210f92ea7a4000c5d75-0.json 2>/dev/null | json_pp
{
- "chaintipHash" : "00000000fb01a7f3745a717f8caebee056c484e6e0bfe4a9591c235bb70506fb",
"chainHeight" : 325347,
+ "chaintipHash" : "00000000fb01a7f3745a717f8caebee056c484e6e0bfe4a9591c235bb70506fb",
+ "bitmap": "1",
"utxos" : [
{
+ "txvers" : 1
+ "height" : 2147483647,
+ "value" : 8.8687,
"scriptPubKey" : {
- "addresses" : [
- "mi7as51dvLJsizWnTMurtRmrP8hG2m1XvD"
- ],
- "type" : "pubkeyhash",
+ "asm" : "OP_DUP OP_HASH160 1c7cebb529b86a04c683dfa87be49de35bcf589e OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9141c7cebb529b86a04c683dfa87be49de35bcf589e88ac",
"reqSigs" : 1,
- "asm" : "OP_DUP OP_HASH160 1c7cebb529b86a04c683dfa87be49de35bcf589e OP_EQUALVERIFY OP_CHECKSIG"
- },
- "value" : 8.8687,
- "height" : 2147483647,
- "txvers" : 1
+ "type" : "pubkeyhash",
+ "addresses" : [
+ "mi7as51dvLJsizWnTMurtRmrP8hG2m1XvD"
+ ]
+ }
}
- ],
- "bitmap" : "1"
+ ]
}
```
@@ -87,6 +89,8 @@ Only supports JSON as output format.
* size : (numeric) the number of transactions in the TX mempool
* bytes : (numeric) size of the TX mempool in bytes
* usage : (numeric) total TX mempool memory usage
+* maxmempool : (numeric) maximum memory usage for the mempool in bytes
+* mempoolminfee : (numeric) minimum feerate (BTC per KB) for tx to be accepted
`GET /rest/mempool/contents.json`
diff --git a/src/.clang-format b/src/.clang-format
index 5918819d13..2d2ee67035 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -23,7 +23,6 @@ ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
-ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, BOOST_REVERSE_FOREACH ]
IndentCaseLabels: false
IndentFunctionDeclarationAfterType: false
IndentWidth: 4
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b9dd89f68..06b09404a7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -124,9 +124,11 @@ BITCOIN_CORE_H = \
pow.h \
protocol.h \
random.h \
+ reverse_iterator.h \
reverselock.h \
rpc/blockchain.h \
rpc/client.h \
+ rpc/mining.h \
rpc/protocol.h \
rpc/server.h \
rpc/register.h \
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 942942c299..f8956508f6 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -5,7 +5,6 @@
#include "bench.h"
#include "wallet/wallet.h"
-#include <boost/foreach.hpp>
#include <set>
static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<COutput>& vCoins)
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 6093f78fb1..c6d5db786e 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -77,7 +77,7 @@ static int AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
- strUsage += HelpMessageOpt("rbfoptin(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"));
+ strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"));
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " +
_("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " +
@@ -673,7 +673,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxVersion(tx, commandVal);
else if (command == "locktime")
MutateTxLocktime(tx, commandVal);
- else if (command == "rbfoptin") {
+ else if (command == "replaceable") {
MutateTxRBFOptIn(tx, commandVal);
}
diff --git a/src/bloom.cpp b/src/bloom.cpp
index cc3baa9185..fa884f0bf3 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -14,7 +14,6 @@
#include <math.h>
#include <stdlib.h>
-#include <boost/foreach.hpp>
#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552
diff --git a/src/chain.cpp b/src/chain.cpp
index 8d4c4e7dea..ffd58d471d 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -148,3 +148,22 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr
}
return sign * r.GetLow64();
}
+
+/** Find the last common ancestor two blocks have.
+ * Both pa and pb must be non-NULL. */
+const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
+ if (pa->nHeight > pb->nHeight) {
+ pa = pa->GetAncestor(pb->nHeight);
+ } else if (pb->nHeight > pa->nHeight) {
+ pb = pb->GetAncestor(pa->nHeight);
+ }
+
+ while (pa != pb && pa && pb) {
+ pa = pa->pprev;
+ pb = pb->pprev;
+ }
+
+ // Eventually all chain branches meet at the genesis block.
+ assert(pa == pb);
+ return pa;
+}
diff --git a/src/chain.h b/src/chain.h
index de120d2d75..c5304b7d6f 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -362,6 +362,9 @@ public:
arith_uint256 GetBlockProof(const CBlockIndex& block);
/** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&);
+/** Find the forking point between two chain tips. */
+const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb);
+
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 13b5876530..e6b5fb72a7 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -6,12 +6,12 @@
#include "chain.h"
#include "chainparams.h"
+#include "reverse_iterator.h"
#include "validation.h"
#include "uint256.h"
#include <stdint.h>
-#include <boost/foreach.hpp>
namespace Checkpoints {
@@ -19,7 +19,7 @@ namespace Checkpoints {
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;
- BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
+ for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
{
const uint256& hash = i.second;
BlockMap::const_iterator t = mapBlockIndex.find(hash);
diff --git a/src/checkqueue.h b/src/checkqueue.h
index d7b7b836dc..408e278d21 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -10,7 +10,6 @@
#include <algorithm>
#include <vector>
-#include <boost/foreach.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>
diff --git a/src/coins.cpp b/src/coins.cpp
index 3113b7755d..b5dc6197bd 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -12,6 +12,7 @@
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
+std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }
@@ -25,6 +26,7 @@ CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
+std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
@@ -85,13 +87,14 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
}
-void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
+void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check) {
bool fCoinbase = tx.IsCoinBase();
const uint256& txid = tx.GetHash();
for (size_t i = 0; i < tx.vout.size(); ++i) {
- // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
+ bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
+ // Always set the possible_overwrite flag to AddCoin for coinbase txn, in order to correctly
// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
- cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase);
+ cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
}
}
diff --git a/src/coins.h b/src/coins.h
index 077545a55b..efb5ce869c 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -157,6 +157,12 @@ public:
//! Retrieve the block hash whose state this CCoinsView currently represents
virtual uint256 GetBestBlock() const;
+ //! Retrieve the range of blocks that may have been only partially written.
+ //! If the database is in a consistent state, the result is the empty vector.
+ //! Otherwise, a two-element vector is returned consisting of the new and
+ //! the old block hash, in that order.
+ virtual std::vector<uint256> GetHeadBlocks() const;
+
//! Do a bulk modification (multiple Coin changes + BestBlock change).
//! The passed mapCoins can be modified.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
@@ -183,6 +189,7 @@ public:
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
+ std::vector<uint256> GetHeadBlocks() const override;
void SetBackend(CCoinsView &viewIn);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
CCoinsViewCursor *Cursor() const override;
@@ -291,12 +298,17 @@ private:
};
//! Utility function to add all of a transaction's outputs to a cache.
-// It assumes that overwrites are only possible for coinbase transactions,
+// When check is false, this assumes that overwrites are only possible for coinbase transactions.
+// When check is true, the underlying view may be queried to determine whether an addition is
+// an overwrite.
// TODO: pass in a boolean to limit these possible overwrites to known
// (pre-BIP34) cases.
-void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight);
+void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false);
//! Utility function to find any unspent output with a given txid.
+// This function can be quite expensive because in the event of a transaction
+// which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK
+// lookups to database, so it should be used with care.
const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid);
#endif // BITCOIN_COINS_H
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 553ef44874..7f38e9e565 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -15,7 +15,6 @@
#include "utilmoneystr.h"
#include "utilstrencodings.h"
-#include <boost/foreach.hpp>
std::string FormatScript(const CScript& script)
{
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 053702f843..a207d5ece4 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -18,7 +18,6 @@
#include <stdio.h>
#include <boost/algorithm/string.hpp> // boost::trim
-#include <boost/foreach.hpp>
/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
@@ -47,11 +46,11 @@ public:
HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
{
}
- const char* Name()
+ const char* Name() override
{
return "HTTP";
}
- RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis)
+ RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
{
return new HTTPRPCTimer(base, func, millis);
}
@@ -92,35 +91,32 @@ static bool multiUserAuthorized(std::string strUserPass)
std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
- if (gArgs.IsArgSet("-rpcauth")) {
+ for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
//Search for multi-user login/pass "rpcauth" from config
- for (std::string strRPCAuth : gArgs.GetArgs("-rpcauth"))
- {
- std::vector<std::string> vFields;
- boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
- if (vFields.size() != 3) {
- //Incorrect formatting in config file
- continue;
- }
-
- std::string strName = vFields[0];
- if (!TimingResistantEqual(strName, strUser)) {
- continue;
- }
-
- std::string strSalt = vFields[1];
- std::string strHash = vFields[2];
-
- static const unsigned int KEY_SIZE = 32;
- unsigned char out[KEY_SIZE];
-
- CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
- std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
- std::string strHashFromPass = HexStr(hexvec);
-
- if (TimingResistantEqual(strHashFromPass, strHash)) {
- return true;
- }
+ std::vector<std::string> vFields;
+ boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
+ if (vFields.size() != 3) {
+ //Incorrect formatting in config file
+ continue;
+ }
+
+ std::string strName = vFields[0];
+ if (!TimingResistantEqual(strName, strUser)) {
+ continue;
+ }
+
+ std::string strSalt = vFields[1];
+ std::string strHash = vFields[2];
+
+ static const unsigned int KEY_SIZE = 32;
+ unsigned char out[KEY_SIZE];
+
+ CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
+ std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
+ std::string strHashFromPass = HexStr(hexvec);
+
+ if (TimingResistantEqual(strHashFromPass, strHash)) {
+ return true;
}
}
return false;
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 1557cf98f8..1c53d8d49d 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -46,7 +46,7 @@ public:
req(std::move(_req)), path(_path), func(_func)
{
}
- void operator()()
+ void operator()() override
{
func(req.get(), path);
}
@@ -196,18 +196,16 @@ static bool InitHTTPAllowList()
LookupHost("::1", localv6, false);
rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
- if (gArgs.IsArgSet("-rpcallowip")) {
- for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
- CSubNet subnet;
- LookupSubNet(strAllow.c_str(), subnet);
- if (!subnet.IsValid()) {
- uiInterface.ThreadSafeMessageBox(
- strprintf("Invalid -rpcallowip subnet specification: %s. Valid 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).", strAllow),
- "", CClientUIInterface::MSG_ERROR);
- return false;
- }
- rpc_allow_subnets.push_back(subnet);
+ for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
+ CSubNet subnet;
+ LookupSubNet(strAllow.c_str(), subnet);
+ if (!subnet.IsValid()) {
+ uiInterface.ThreadSafeMessageBox(
+ strprintf("Invalid -rpcallowip subnet specification: %s. Valid 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).", strAllow),
+ "", CClientUIInterface::MSG_ERROR);
+ return false;
}
+ rpc_allow_subnets.push_back(subnet);
}
std::string strAllowed;
for (const CSubNet& subnet : rpc_allow_subnets)
diff --git a/src/init.cpp b/src/init.cpp
index 88084cbeec..12f29a4ca8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -215,6 +215,19 @@ void Shutdown()
fFeeEstimatesInitialized = false;
}
+ // FlushStateToDisk generates a SetBestChain callback, which we should avoid missing
+ FlushStateToDisk();
+
+ // After there are no more peers/RPC left to give us new data which may generate
+ // CValidationInterface callbacks, flush them...
+ GetMainSignals().FlushBackgroundCallbacks();
+
+ // Any future callbacks will be dropped. This should absolutely be safe - if
+ // missing a callback results in an unrecoverable situation, unclean shutdown
+ // would too. The only reason to do the above flushes is to let the wallet catch
+ // up with our current chain to avoid any strange pruning edge cases and make
+ // next startup faster by avoiding rescan.
+
{
LOCK(cs_main);
if (pcoinsTip != NULL) {
@@ -251,6 +264,7 @@ void Shutdown()
}
#endif
UnregisterAllValidationInterfaces();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
delete pwallet;
@@ -336,6 +350,9 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
}
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
+ if (showDebug) {
+ strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize));
+ }
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
if (showDebug)
strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER));
@@ -444,7 +461,7 @@ std::string HelpMessage(HelpMessageMode mode)
{
strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS));
strUsage += HelpMessageOpt("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)");
- strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE));
+ strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE));
strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE));
}
strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"),
@@ -880,9 +897,7 @@ bool AppInitParameterInteraction()
}
// -bind and -whitebind can't be set when not listening
- size_t nUserBind =
- (gArgs.IsArgSet("-bind") ? gArgs.GetArgs("-bind").size() : 0) +
- (gArgs.IsArgSet("-whitebind") ? gArgs.GetArgs("-whitebind").size() : 0);
+ size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError("Cannot set -bind or -whitebind together with -listen=0");
}
@@ -920,15 +935,13 @@ bool AppInitParameterInteraction()
}
// Now remove the logging categories which were explicitly excluded
- if (gArgs.IsArgSet("-debugexclude")) {
- for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
- uint32_t flag = 0;
- if (!GetLogCategory(&flag, &cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
- continue;
- }
- logCategories &= ~flag;
+ for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
+ uint32_t flag = 0;
+ if (!GetLogCategory(&flag, &cat)) {
+ InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
+ continue;
}
+ logCategories &= ~flag;
}
// Check for -debugnet
@@ -1192,6 +1205,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
InitSignatureCache();
+ InitScriptExecutionCache();
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
@@ -1203,6 +1217,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
* that the server is there and will be ready later). Warmup mode will
@@ -1238,13 +1254,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
- if (gArgs.IsArgSet("-uacomment")) {
- for (std::string cmt : gArgs.GetArgs("-uacomment"))
- {
- if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
- return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
- uacomments.push_back(cmt);
- }
+ for (const std::string& cmt : gArgs.GetArgs("-uacomment")) {
+ if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
+ return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
+ uacomments.push_back(cmt);
}
strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
@@ -1317,14 +1330,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
fDiscover = GetBoolArg("-discover", true);
fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
- if (gArgs.IsArgSet("-externalip")) {
- for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
- CService addrLocal;
- if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
- AddLocal(addrLocal, LOCAL_MANUAL);
- else
- return InitError(ResolveErrMsg("externalip", strAddr));
- }
+ for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
+ CService addrLocal;
+ if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
+ AddLocal(addrLocal, LOCAL_MANUAL);
+ else
+ return InitError(ResolveErrMsg("externalip", strAddr));
}
#if ENABLE_ZMQ
@@ -1364,7 +1375,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
bool fLoaded = false;
- while (!fLoaded) {
+ while (!fLoaded && !fRequestShutdown) {
bool fReset = fReindex;
std::string strLoadError;
@@ -1382,7 +1393,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
- pcoinsTip = new CCoinsViewCache(pcoinscatcher);
if (fReindex) {
pblocktree->WriteReindexing(true);
@@ -1396,6 +1406,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}
}
+ if (fRequestShutdown) break;
if (!LoadBlockIndex(chainparams)) {
strLoadError = _("Error loading block database");
@@ -1426,6 +1437,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}
+ if (!ReplayBlocks(chainparams, pcoinsdbview)) {
+ strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
+ break;
+ }
+ pcoinsTip = new CCoinsViewCache(pcoinscatcher);
+ LoadChainTip(chainparams);
+
if (!fReindex && chainActive.Tip() != NULL) {
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(chainparams)) {
@@ -1466,7 +1484,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
fLoaded = true;
} while(false);
- if (!fLoaded) {
+ if (!fLoaded && !fRequestShutdown) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
@@ -1553,10 +1571,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
std::vector<fs::path> vImportFiles;
- if (gArgs.IsArgSet("-loadblock"))
- {
- for (const std::string& strFile : gArgs.GetArgs("-loadblock"))
- vImportFiles.push_back(strFile);
+ for (const std::string& strFile : gArgs.GetArgs("-loadblock")) {
+ vImportFiles.push_back(strFile);
}
threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));
@@ -1598,36 +1614,30 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
- if (gArgs.IsArgSet("-bind")) {
- for (const std::string& strBind : gArgs.GetArgs("-bind")) {
- CService addrBind;
- if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) {
- return InitError(ResolveErrMsg("bind", strBind));
- }
- connOptions.vBinds.push_back(addrBind);
+ for (const std::string& strBind : gArgs.GetArgs("-bind")) {
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) {
+ return InitError(ResolveErrMsg("bind", strBind));
}
+ connOptions.vBinds.push_back(addrBind);
}
- if (gArgs.IsArgSet("-whitebind")) {
- for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
- CService addrBind;
- if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
- return InitError(ResolveErrMsg("whitebind", strBind));
- }
- if (addrBind.GetPort() == 0) {
- return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
- }
- connOptions.vWhiteBinds.push_back(addrBind);
+ for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
+ return InitError(ResolveErrMsg("whitebind", strBind));
}
+ if (addrBind.GetPort() == 0) {
+ return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
+ }
+ connOptions.vWhiteBinds.push_back(addrBind);
}
- if (gArgs.IsArgSet("-whitelist")) {
- for (const auto& net : gArgs.GetArgs("-whitelist")) {
- CSubNet subnet;
- LookupSubNet(net.c_str(), subnet);
- if (!subnet.IsValid())
- return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
- connOptions.vWhitelistedRange.push_back(subnet);
- }
+ for (const auto& net : gArgs.GetArgs("-whitelist")) {
+ CSubNet subnet;
+ LookupSubNet(net.c_str(), subnet);
+ if (!subnet.IsValid())
+ return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
+ connOptions.vWhitelistedRange.push_back(subnet);
}
if (gArgs.IsArgSet("-seednode")) {
diff --git a/src/keystore.h b/src/keystore.h
index a2621f2de4..965ae0c79a 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -60,9 +60,9 @@ protected:
WatchOnlySet setWatchOnly;
public:
- bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
- bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
- bool HaveKey(const CKeyID &address) const
+ bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
+ bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ bool HaveKey(const CKeyID &address) const override
{
bool result;
{
@@ -71,7 +71,7 @@ public:
}
return result;
}
- void GetKeys(std::set<CKeyID> &setAddress) const
+ void GetKeys(std::set<CKeyID> &setAddress) const override
{
setAddress.clear();
{
@@ -84,7 +84,7 @@ public:
}
}
}
- bool GetKey(const CKeyID &address, CKey &keyOut) const
+ bool GetKey(const CKeyID &address, CKey &keyOut) const override
{
{
LOCK(cs_KeyStore);
@@ -97,14 +97,14 @@ public:
}
return false;
}
- virtual bool AddCScript(const CScript& redeemScript);
- virtual bool HaveCScript(const CScriptID &hash) const;
- virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
+ virtual bool AddCScript(const CScript& redeemScript) override;
+ virtual bool HaveCScript(const CScriptID &hash) const override;
+ virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
- virtual bool AddWatchOnly(const CScript &dest);
- virtual bool RemoveWatchOnly(const CScript &dest);
- virtual bool HaveWatchOnly(const CScript &dest) const;
- virtual bool HaveWatchOnly() const;
+ virtual bool AddWatchOnly(const CScript &dest) override;
+ virtual bool RemoveWatchOnly(const CScript &dest) override;
+ virtual bool HaveWatchOnly(const CScript &dest) const override;
+ virtual bool HaveWatchOnly() const override;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
diff --git a/src/memusage.h b/src/memusage.h
index 710120d285..93fd6a0eb5 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -15,7 +15,6 @@
#include <unordered_map>
#include <unordered_set>
-#include <boost/foreach.hpp>
namespace memusage
{
diff --git a/src/net.cpp b/src/net.cpp
index 91a62626a2..301cf58b87 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1677,7 +1677,7 @@ void CConnman::ProcessOneShot()
void CConnman::ThreadOpenConnections()
{
// Connect to specific addresses
- if (gArgs.IsArgSet("-connect") && gArgs.GetArgs("-connect").size() > 0)
+ if (gArgs.IsArgSet("-connect"))
{
for (int64_t nLoop = 0;; nLoop++)
{
@@ -1903,8 +1903,7 @@ void CConnman::ThreadOpenAddedConnections()
{
{
LOCK(cs_vAddedNodes);
- if (gArgs.IsArgSet("-addnode"))
- vAddedNodes = gArgs.GetArgs("-addnode");
+ vAddedNodes = gArgs.GetArgs("-addnode");
}
while (true)
diff --git a/src/net.h b/src/net.h
index dc25e7a5dd..b9a11c62f2 100644
--- a/src/net.h
+++ b/src/net.h
@@ -33,7 +33,6 @@
#include <arpa/inet.h>
#endif
-#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
class CScheduler;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 8fc6f6f95e..a743f04dd1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -22,6 +22,7 @@
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "random.h"
+#include "reverse_iterator.h"
#include "tinyformat.h"
#include "txmempool.h"
#include "ui_interface.h"
@@ -452,25 +453,6 @@ bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
return false;
}
-/** Find the last common ancestor two blocks have.
- * Both pa and pb must be non-NULL. */
-const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
- if (pa->nHeight > pb->nHeight) {
- pa = pa->GetAncestor(pb->nHeight);
- } else if (pb->nHeight > pa->nHeight) {
- pb = pb->GetAncestor(pa->nHeight);
- }
-
- while (pa != pb && pa && pb) {
- pa = pa->pprev;
- pb = pb->pprev;
- }
-
- // Eventually all chain branches meet at the genesis block.
- assert(pa == pb);
- return pa;
-}
-
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
* at most count entries. */
void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
@@ -846,7 +828,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
// Relay inventory, but don't relay old inventory during initial block download.
connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
- BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
+ for (const uint256& hash : reverse_iterate(vHashes)) {
pnode->PushBlockHash(hash);
}
}
@@ -2357,7 +2339,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
} else {
std::vector<CInv> vGetData;
// Download as much as possible, from earliest to latest.
- BOOST_REVERSE_FOREACH(const CBlockIndex *pindex, vToFetch) {
+ for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
// Can't download any more from this peer
break;
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 771491770e..c39a2d55c3 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -36,6 +36,20 @@ std::string StringForFeeReason(FeeReason reason) {
return reason_string->second;
}
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
+ static const std::map<std::string, FeeEstimateMode> fee_modes = {
+ {"UNSET", FeeEstimateMode::UNSET},
+ {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
+ {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
+ };
+ auto mode = fee_modes.find(mode_string);
+
+ if (mode == fee_modes.end()) return false;
+
+ fee_estimate_mode = mode->second;
+ return true;
+}
+
/**
* We will instantiate an instance of this class to track transactions that were
* included in a block. We will lump transactions into a bucket according to their
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 2029ce3744..fea279e477 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -90,6 +90,15 @@ enum class FeeReason {
std::string StringForFeeReason(FeeReason reason);
+/* Used to determine type of fee estimation requested */
+enum class FeeEstimateMode {
+ UNSET, //! Use default settings based on other criteria
+ ECONOMICAL, //! Force estimateSmartFee to use non-conservative estimates
+ CONSERVATIVE, //! Force estimateSmartFee to use conservative estimates
+};
+
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
+
/* Used to return detailed information about a feerate bucket */
struct EstimatorBucket
{
@@ -197,7 +206,7 @@ public:
* the closest target where one can be given. 'conservative' estimates are
* valid over longer time horizons also.
*/
- CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative = true) const;
+ CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const;
/** Return a specific fee estimate calculation with a given success
* threshold and time horizon, and optionally return detailed data about
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 5f68c09a86..2f78d2f347 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -13,7 +13,6 @@
#include "util.h"
#include "utilstrencodings.h"
-#include <boost/foreach.hpp>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
diff --git a/src/prevector.h b/src/prevector.h
index dc17e7ce4b..02d860bb00 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -132,7 +132,7 @@ public:
typedef const T* pointer;
typedef const T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
- const_reverse_iterator(T* ptr_) : ptr(ptr_) {}
+ const_reverse_iterator(const T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {}
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr; }
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index d3ad24da01..2fa032abdc 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -10,7 +10,6 @@
#include "base58.h"
#include "wallet/wallet.h"
-#include <boost/foreach.hpp>
#include <QFont>
#include <QDebug>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 6d8760c071..8a745cadce 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -578,6 +578,7 @@ int main(int argc, char *argv[])
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType< CAmount >("CAmount");
+ qRegisterMetaType< std::function<void(void)> >("std::function<void(void)>");
/// 3. Application identification
// must be set before OptionsModel is initialized or translations are loaded,
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index a1e5cccc0b..b3d2cf1d55 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -21,9 +21,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"A fee rate (in %s/kB) that will be used when fee estimation has insufficient "
"data (default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Accept connections from outside (default: 1 if no -proxy or -connect/-"
-"noconnect)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Accept relayed transactions received from whitelisted peers even when not "
"relaying transactions (default: %d)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -37,14 +34,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Bind to given address and whitelist peers connecting to it. Use [host]:port "
"notation for IPv6"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Bind to given address to listen for JSON-RPC connections. Use [host]:port "
-"notation for IPv6. This option can be specified multiple times (default: "
-"bind to all interfaces)"),
+"Bind to given address to listen for JSON-RPC connections. This option is "
+"ignored unless -rpcallowip is also passed. Port is optional and overrides -"
+"rpcport. Use [host]:port notation for IPv6. This option can be specified "
+"multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -"
+"rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. %s is probably already running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Connect only to the specified node(s); -noconnect or -connect=0 alone to "
-"disable automatic connections"),
+"Connect only to the specified node(s); -connect=0 disables automatic "
+"connections"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Create new files with system default permissions, instead of umask 077 (only "
"effective with disabled wallet functionality)"),
@@ -62,13 +61,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Equivalent bytes per sigop in transactions for relay and mining (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Error loading %s: You can't enable HD on a already existing non-HD wallet"),
+"Error loading %s: You can't enable HD on an already existing non-HD wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error reading %s! All keys read correctly, but transaction data or address "
"book entries might be missing or incorrect."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Exclude debugging information for a category. Can be used in conjunction "
+"with -debug=1 to output debug logs for all categories except one or more "
+"specified categories."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Execute command when a relevant alert is received or we see a really long "
"fork (%s in cmd is replaced by message)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -134,7 +137,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"reindex (download the whole blockchain again in case of pruned node)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Query for peer addresses via DNS lookup, if low on addresses (default: 1 "
-"unless -connect/-noconnect)"),
+"unless -connect used)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Randomize credentials for every proxy connection. This enables Tor stream "
"isolation (default: %u)"),
@@ -154,8 +157,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Set lowest fee rate (in %s/kB) for transactions to be included in block "
"creation. (default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Set the number of script verification threads (%u to %d, 0 = auto, <0 = "
"leave that many cores free, default: %d)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -186,6 +187,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = "
"no limit (default: %d)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Unable to replay blocks. You will need to rebuild the database using -"
+"reindex-chainstate."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to rewind the database to a pre-fork state. You will need to "
"redownload the blockchain"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -225,6 +229,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: We do not appear to fully agree with our peers! You may need to "
"upgrade, or other nodes may need to upgrade."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Whether to save the mempool on shutdown and load on restart (default: %u)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR "
"notated network (e.g. 1.2.3.0/24). Can be specified multiple times."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -235,13 +241,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"mode. This will redownload the entire blockchain"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"You need to rebuild the database using -reindex-chainstate to change -txindex"),
+QT_TRANSLATE_NOOP("bitcoin-core", "%d of last 100 blocks have unexpected version"),
QT_TRANSLATE_NOOP("bitcoin-core", "%s corrupt, salvage failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"),
QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "(press q to shutdown and continue later)"),
QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"),
+QT_TRANSLATE_NOOP("bitcoin-core", "-wallet parameter must only specify a filename (not a path)"),
QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Accept public REST requests (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"),
QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"),
@@ -274,10 +284,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environmen
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet corrupted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer version of %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on a already existing HD wallet"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on an already existing HD wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"),
@@ -291,18 +302,19 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong da
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -%s=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid characters in -wallet filename"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Keep the transaction memory pool below <n> megabytes (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Keypool ran out, please call keypoolrefill first"),
QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: %u or testnet: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."),
@@ -329,12 +341,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state from the currently indexe
QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."),
QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Relay non-P2SH multisig (default: %u)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions on startup"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Rewinding blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"),
QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions as zero-fee transactions if possible (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions with full-RBF opt-in enabled (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (%d to %d, default: %d)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: %u)"),
@@ -374,13 +386,15 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'")
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -debugnet ignored, use -debug=net."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -tor found, use -onion."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format on startup"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use the test chain"),
QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."),
QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet debugging/testing options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"),
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index af9a888d94..c19420beb5 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -490,6 +490,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
else nBytesInputs += 148;
}
+ bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, coinControl->signalRbf);
+
// calculation
if (nQuantity > 0)
{
@@ -510,7 +512,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator);
+ nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */, false /* ignoreGlobalPayTxFee */, conservative_estimate);
if (nPayAmount > 0)
{
@@ -585,7 +587,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (payTxFee.GetFeePerK() > 0)
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
else {
- dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000;
+ dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool, conservative_estimate).GetFeePerK()) / 1000;
}
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index f62f1e4a73..9183075067 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -127,7 +127,7 @@
<context>
<name>AddressTableModel</name>
<message>
- <location filename="../addresstablemodel.cpp" line="+170"/>
+ <location filename="../addresstablemodel.cpp" line="+169"/>
<source>Label</source>
<translation type="unfinished"></translation>
</message>
@@ -304,12 +304,12 @@
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+427"/>
+ <location line="+429"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-505"/>
+ <location line="-507"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -404,7 +404,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+357"/>
+ <location line="+359"/>
<source>Click to disable network activity.</source>
<translation type="unfinished"></translation>
</message>
@@ -429,7 +429,7 @@
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="-508"/>
+ <location line="-510"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -459,12 +459,12 @@
<translation>&amp;Verify message...</translation>
</message>
<message>
- <location line="+514"/>
+ <location line="+516"/>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
- <location line="-739"/>
+ <location line="-741"/>
<source>Wallet</source>
<translation>Wallet</translation>
</message>
@@ -549,7 +549,7 @@
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+354"/>
+ <location line="+356"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -610,12 +610,12 @@
<translation>Up to date</translation>
</message>
<message>
- <location line="-438"/>
+ <location line="-440"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+197"/>
+ <location line="+199"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
@@ -690,7 +690,7 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+518"/>
+ <location filename="../bitcoin.cpp" line="+524"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation type="unfinished"></translation>
</message>
@@ -783,7 +783,7 @@
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+55"/>
+ <location filename="../coincontroldialog.cpp" line="+54"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -849,7 +849,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+183"/>
+ <location line="+155"/>
<source>yes</source>
<translation type="unfinished"></translation>
</message>
@@ -956,7 +956,7 @@
<context>
<name>FreespaceChecker</name>
<message>
- <location filename="../intro.cpp" line="+78"/>
+ <location filename="../intro.cpp" line="+76"/>
<source>A new data directory will be created.</source>
<translation>A new data directory will be created.</translation>
</message>
@@ -1068,12 +1068,22 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+157"/>
+ <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+10"/>
- <source>%1 will download and store a copy of the Bitcoin block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source>
+ <source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+10"/>
+ <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-160"/>
<source>Use the default data directory</source>
<translation>Use the default data directory</translation>
</message>
@@ -1083,7 +1093,32 @@
<translation>Use a custom data directory:</translation>
</message>
<message>
- <location filename="../intro.cpp" line="+94"/>
+ <location filename="../intro.cpp" line="+20"/>
+ <source>Bitcoin</source>
+ <translation type="unfinished">Bitcoin</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+70"/>
<source>Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<translation type="unfinished"></translation>
</message>
@@ -1257,7 +1292,14 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+94"/>
+ <location line="-118"/>
+ <location line="+23"/>
+ <location line="+23"/>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+166"/>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<translation type="unfinished"></translation>
</message>
@@ -1278,7 +1320,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+43"/>
+ <location line="+45"/>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Open Configuration File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
<source>Reset all client options to default.</source>
<translation>Reset all client options to default.</translation>
</message>
@@ -1288,7 +1340,7 @@
<translation>&amp;Reset Options</translation>
</message>
<message>
- <location line="-514"/>
+ <location line="-529"/>
<source>&amp;Network</source>
<translation>&amp;Network</translation>
</message>
@@ -1366,14 +1418,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<location line="+23"/>
- <location line="+23"/>
- <source>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-36"/>
<source>IPv4</source>
<translation type="unfinished"></translation>
</message>
@@ -1458,7 +1503,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+612"/>
+ <location line="+646"/>
<source>&amp;OK</source>
<translation>&amp;OK</translation>
</message>
@@ -1468,7 +1513,7 @@
<translation>&amp;Cancel</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+86"/>
+ <location filename="../optionsdialog.cpp" line="+84"/>
<source>default</source>
<translation>default</translation>
</message>
@@ -1484,22 +1529,42 @@
</message>
<message>
<location line="+1"/>
- <location line="+43"/>
+ <location line="+55"/>
<source>Client restart required to activate changes.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-43"/>
+ <location line="-55"/>
<source>Client will be shut down. Do you want to proceed?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+47"/>
+ <location line="+15"/>
+ <source>Configuration options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Error</source>
+ <translation type="unfinished">Error</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>The configuration file could not be opened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+38"/>
<source>This change would require a client restart.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
+ <location line="+28"/>
<source>The supplied proxy address is invalid.</source>
<translation>The supplied proxy address is invalid.</translation>
</message>
@@ -1755,12 +1820,12 @@
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+136"/>
+ <location filename="../guiutil.cpp" line="+130"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+759"/>
+ <location line="+766"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1850,7 +1915,7 @@
</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+172"/>
+ <location filename="../bitcoin.cpp" line="+173"/>
<source>%1 didn&apos;t yet exit safely...</source>
<translation type="unfinished"></translation>
</message>
@@ -2007,7 +2072,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+404"/>
+ <location line="+324"/>
+ <source>&amp;Reset</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+80"/>
<location line="+558"/>
<source>Received</source>
<translation type="unfinished"></translation>
@@ -2030,8 +2100,8 @@
</message>
<message>
<location line="+60"/>
- <location filename="../rpcconsole.cpp" line="+456"/>
- <location line="+719"/>
+ <location filename="../rpcconsole.cpp" line="+458"/>
+ <location line="+728"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
@@ -2157,12 +2227,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+52"/>
- <source>&amp;Clear</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+16"/>
+ <location line="+68"/>
<source>Totals</source>
<translation type="unfinished"></translation>
</message>
@@ -2187,7 +2252,7 @@
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-214"/>
+ <location filename="../rpcconsole.cpp" line="-223"/>
<source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
@@ -2225,23 +2290,23 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+126"/>
+ <location line="+135"/>
<source>Welcome to the %1 RPC console.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Use up and down arrows to navigate history, and &lt;b&gt;Ctrl-L&lt;/b&gt; to clear screen.</source>
- <translation>Use up and down arrows to navigate history, and &lt;b&gt;Ctrl-L&lt;/b&gt; to clear screen.</translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Type &lt;b&gt;help&lt;/b&gt; for an overview of available commands.</source>
<translation>Type &lt;b&gt;help&lt;/b&gt; for an overview of available commands.</translation>
</message>
<message>
- <location line="+2"/>
- <source>WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command.</source>
+ <location line="-1"/>
+ <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2494,7 +2559,7 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+29"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+28"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -2509,7 +2574,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+40"/>
+ <location line="+39"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -2533,7 +2598,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+554"/>
+ <location filename="../sendcoinsdialog.cpp" line="+565"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2608,7 +2673,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+37"/>
+ <location line="+24"/>
+ <source>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+26"/>
<source>collapse fee-settings</source>
<translation type="unfinished"></translation>
</message>
@@ -2619,22 +2694,16 @@
</message>
<message>
<location line="-3"/>
- <location line="+16"/>
<source>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;total at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-64"/>
+ <location line="-48"/>
<source>Hide</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+67"/>
- <source>total at least</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+30"/>
+ <location line="+84"/>
<location line="+13"/>
<source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
<translation type="unfinished"></translation>
@@ -2670,7 +2739,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+102"/>
+ <location line="+30"/>
+ <source>Request Replace-By-Fee</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Indicates that the sender may wish to replace this transaction with a new one paying higher fees (prior to being confirmed).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+79"/>
<source>Send to multiple recipients at once</source>
<translation>Send to multiple recipients at once</translation>
</message>
@@ -2685,17 +2764,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-876"/>
+ <location line="-895"/>
<source>Dust:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+691"/>
+ <location line="+700"/>
<source>Confirmation time target:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+188"/>
+ <location line="+198"/>
<source>Clear &amp;All</source>
<translation>Clear &amp;All</translation>
</message>
@@ -2715,7 +2794,7 @@
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-486"/>
+ <location filename="../sendcoinsdialog.cpp" line="-497"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -2750,7 +2829,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+205"/>
+ <location line="+209"/>
<location line="+5"/>
<location line="+5"/>
<location line="+4"/>
@@ -2778,7 +2857,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+5"/>
+ <source>This transaction signals replaceability (optin-RBF).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>Confirm send coins</source>
<translation type="unfinished"></translation>
</message>
@@ -2828,7 +2912,7 @@
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+67"/>
+ <location line="+63"/>
<source>%n block(s)</source>
<translation>
<numerusform>%n block</numerusform>
@@ -2836,12 +2920,12 @@
</translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+24"/>
<source>Pay only the required fee of %1</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+25"/>
+ <location line="+30"/>
<source>Estimated to begin confirmation within %n block(s).</source>
<translation>
<numerusform>Estimated to begin confirmation within %n block.</numerusform>
@@ -2849,7 +2933,7 @@
</translation>
</message>
<message>
- <location line="+102"/>
+ <location line="+103"/>
<source>Warning: Invalid Bitcoin address</source>
<translation type="unfinished"></translation>
</message>
@@ -2986,7 +3070,7 @@
<context>
<name>SendConfirmationDialog</name>
<message>
- <location filename="../sendcoinsdialog.cpp" line="+95"/>
+ <location filename="../sendcoinsdialog.cpp" line="+86"/>
<location line="+5"/>
<source>Yes</source>
<translation type="unfinished"></translation>
@@ -3198,7 +3282,7 @@
<context>
<name>TrafficGraphWidget</name>
<message>
- <location filename="../trafficgraphwidget.cpp" line="+79"/>
+ <location filename="../trafficgraphwidget.cpp" line="+80"/>
<source>KB/s</source>
<translation type="unfinished"></translation>
</message>
@@ -3427,7 +3511,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+21"/>
+ <location line="+20"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
@@ -3460,7 +3544,7 @@
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+246"/>
+ <location filename="../transactiontablemodel.cpp" line="+248"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3606,7 +3690,7 @@
<context>
<name>TransactionView</name>
<message>
- <location filename="../transactionview.cpp" line="+69"/>
+ <location filename="../transactionview.cpp" line="+70"/>
<location line="+16"/>
<source>All</source>
<translation type="unfinished"></translation>
@@ -3677,12 +3761,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
+ <location line="+37"/>
<source>Abandon transaction</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>Increase transaction fee</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -3722,7 +3811,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+186"/>
+ <location line="+193"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
@@ -3787,7 +3876,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+147"/>
+ <location line="+166"/>
<source>Range:</source>
<translation type="unfinished"></translation>
</message>
@@ -3816,10 +3905,57 @@
<context>
<name>WalletModel</name>
<message>
- <location filename="../walletmodel.cpp" line="+291"/>
+ <location filename="../walletmodel.cpp" line="+289"/>
<source>Send Coins</source>
<translation type="unfinished">Send Coins</translation>
</message>
+ <message>
+ <location line="+385"/>
+ <location line="+46"/>
+ <location line="+9"/>
+ <source>Fee bump error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-55"/>
+ <source>Increasing transaction fee failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Do you want to increase the fee?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Current fee:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Increase:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>New fee:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Confirm fee bump</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>Can&apos;t sign transaction.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Could not commit transaction</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>WalletView</name>
@@ -3867,7 +4003,7 @@
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+318"/>
+ <location filename="../bitcoinstrings.cpp" line="+330"/>
<source>Options:</source>
<translation>Options:</translation>
</message>
@@ -3877,37 +4013,27 @@
<translation>Specify data directory</translation>
</message>
<message>
- <location line="-90"/>
+ <location line="-92"/>
<source>Connect to a node to retrieve peer addresses, and disconnect</source>
<translation>Connect to a node to retrieve peer addresses, and disconnect</translation>
</message>
<message>
- <location line="+93"/>
+ <location line="+95"/>
<source>Specify your own public address</source>
<translation>Specify your own public address</translation>
</message>
<message>
- <location line="-108"/>
+ <location line="-111"/>
<source>Accept command line and JSON-RPC commands</source>
<translation>Accept command line and JSON-RPC commands</translation>
</message>
<message>
- <location line="-221"/>
- <source>Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+22"/>
- <source>Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+12"/>
+ <location line="-197"/>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+37"/>
+ <location line="+41"/>
<source>If &lt;category&gt; is not supplied or if &lt;category&gt; = 1, output all debugging information.</source>
<translation type="unfinished"></translation>
</message>
@@ -3927,7 +4053,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+132"/>
+ <location line="+140"/>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
<translation type="unfinished"></translation>
</message>
@@ -3937,22 +4063,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+40"/>
+ <location line="+41"/>
<source>Pruning blockstore...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+11"/>
<source>Run in the background as a daemon and accept commands</source>
<translation>Run in the background as a daemon and accept commands</translation>
</message>
<message>
- <location line="+37"/>
+ <location line="+36"/>
<source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-360"/>
+ <location line="-372"/>
<source>Bitcoin Core</source>
<translation type="unfinished">Bitcoin Core</translation>
</message>
@@ -3967,7 +4093,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+3"/>
<source>Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)</source>
<translation type="unfinished"></translation>
</message>
@@ -3977,7 +4103,7 @@
<translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+12"/>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
@@ -3987,17 +4113,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
- <source>Error loading %s: You can&apos;t enable HD on a already existing non-HD wallet</source>
+ <location line="+15"/>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <location line="+5"/>
+ <source>Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+7"/>
<source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source>
<translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation>
</message>
@@ -4032,7 +4158,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+8"/>
+ <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
<source>Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, &gt;%u = automatically prune block files to stay under the specified target size in MiB)</source>
<translation type="unfinished"></translation>
</message>
@@ -4042,7 +4173,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+3"/>
<source>Set the number of script verification threads (%u to %d, 0 = auto, &lt;0 = leave that many cores free, default: %d)</source>
<translation type="unfinished"></translation>
</message>
@@ -4058,6 +4189,11 @@
</message>
<message>
<location line="+15"/>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
<translation type="unfinished"></translation>
</message>
@@ -4087,27 +4223,52 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+3"/>
+ <source>Whether to save the mempool on shutdown and load on restart (default: %u)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+11"/>
<source>You need to rebuild the database using -reindex-chainstate to change -txindex</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
+ <source>%d of last 100 blocks have unexpected version</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>%s corrupt, salvage failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+4"/>
+ <source>(press q to shutdown and continue later)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>-maxmempool must be at least %d MB</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>-wallet parameter must only specify a filename (not a path)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>&lt;category&gt; can be:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+2"/>
+ <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
<source>Append comment to the user agent string</source>
<translation type="unfinished"></translation>
</message>
@@ -4217,12 +4378,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Error loading %s: You can&apos;t disable HD on a already existing HD wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Error loading block database</source>
<translation>Error loading block database</translation>
</message>
@@ -4232,7 +4388,7 @@
<translation>Error opening block database</translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>Error: Disk space is low!</source>
<translation>Error: Disk space is low!</translation>
</message>
@@ -4257,27 +4413,32 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Invalid -onion address: &apos;%s&apos;</source>
+ <location line="+4"/>
+ <source>Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <source>Invalid characters in -wallet filename</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <location line="+3"/>
+ <source>Keep the transaction memory pool below &lt;n&gt; megabytes (default: %u)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+4"/>
- <source>Keep the transaction memory pool below &lt;n&gt; megabytes (default: %u)</source>
+ <source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+1"/>
<source>Loading banlist...</source>
<translation type="unfinished"></translation>
</message>
@@ -4327,12 +4488,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+4"/>
+ <source>Replaying blocks...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Rewinding blocks...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+4"/>
<source>Set database cache size in megabytes (%d to %d, default: %d)</source>
<translation type="unfinished"></translation>
</message>
@@ -4372,7 +4538,17 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+1"/>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+2"/>
+ <source>Upgrading UTXO database</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Use UPnP to map the listening port (default: %u)</source>
<translation type="unfinished"></translation>
</message>
@@ -4392,12 +4568,7 @@
<translation>Verifying blocks...</translation>
</message>
<message>
- <location line="+1"/>
- <source>Verifying wallet...</source>
- <translation>Verifying wallet...</translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Wallet %s resides outside data directory %s</source>
<translation>Wallet %s resides outside data directory %s</translation>
</message>
@@ -4417,7 +4588,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-358"/>
+ <location line="-375"/>
<source>Allow JSON-RPC connections from specified source. Valid for &lt;ip&gt; 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</source>
<translation type="unfinished"></translation>
</message>
@@ -4427,12 +4598,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+9"/>
+ <location line="+14"/>
<source>Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)</source>
<translation type="unfinished"></translation>
</message>
@@ -4447,7 +4613,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+6"/>
<source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source>
<translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation>
</message>
@@ -4477,22 +4643,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+15"/>
+ <location line="+31"/>
<source>The transaction amount is too small to send after the fee has been deducted</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+31"/>
<source>Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+31"/>
+ <location line="+33"/>
<source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source>
<translation type="unfinished"></translation>
</message>
@@ -4502,12 +4663,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+9"/>
<source>(default: %u)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+7"/>
<source>Accept public REST requests (default: %u)</source>
<translation type="unfinished"></translation>
</message>
@@ -4522,11 +4683,21 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+19"/>
+ <source>Error loading %s: You can&apos;t disable HD on an already existing HD wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Error reading from database, shutting down.</source>
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+1"/>
+ <source>Error upgrading chainstate database</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+8"/>
<source>Imports blocks from external blk000??.dat file on startup</source>
<translation type="unfinished"></translation>
@@ -4537,12 +4708,22 @@
<translation>Information</translation>
</message>
<message>
- <location line="+7"/>
- <source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <location line="+3"/>
+ <source>Invalid -onion address or hostname: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>Invalid -proxy address or hostname: &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -4572,7 +4753,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+4"/>
<source>Rescan the block chain for missing wallet transactions on startup</source>
<translation type="unfinished"></translation>
</message>
@@ -4582,11 +4763,6 @@
<translation>Send trace/debug info to console instead of debug.log file</translation>
</message>
<message>
- <location line="+1"/>
- <source>Send transactions as zero-fee transactions if possible (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location line="+7"/>
<source>Show all debugging options (usage: --help -help-debug)</source>
<translation type="unfinished"></translation>
@@ -4642,17 +4818,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+8"/>
<source>Upgrade wallet to latest format on startup</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>Username for JSON-RPC connections</source>
<translation>Username for JSON-RPC connections</translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+2"/>
+ <source>Verifying wallet(s)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
<source>Warning</source>
<translation>Warning</translation>
</message>
@@ -4677,27 +4858,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-73"/>
+ <location line="-75"/>
<source>Password for JSON-RPC connections</source>
<translation>Password for JSON-RPC connections</translation>
</message>
<message>
- <location line="-242"/>
+ <location line="-251"/>
<source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source>
<translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation>
</message>
<message>
- <location line="+170"/>
+ <location line="+177"/>
<source>Allow DNS lookups for -addnode, -seednode and -connect</source>
<translation>Allow DNS lookups for -addnode, -seednode and -connect</translation>
</message>
<message>
- <location line="+58"/>
- <source>Loading addresses...</source>
- <translation>Loading addresses...</translation>
- </message>
- <message>
- <location line="-291"/>
+ <location line="-243"/>
<source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source>
<translation type="unfinished"></translation>
</message>
@@ -4707,7 +4883,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+43"/>
+ <location line="+19"/>
+ <source>Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Connect only to the specified node(s); -connect=0 disables automatic connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+15"/>
<source>Do not keep transactions in the mempool longer than &lt;n&gt; hours (default: %u)</source>
<translation type="unfinished"></translation>
</message>
@@ -4717,7 +4903,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+2"/>
+ <source>Error loading %s: You can&apos;t enable HD on an already existing non-HD wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+26"/>
<source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source>
<translation type="unfinished"></translation>
</message>
@@ -4747,12 +4938,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
- <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+26"/>
+ <location line="+37"/>
<source>Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)</source>
<translation type="unfinished"></translation>
</message>
@@ -4782,7 +4968,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+9"/>
<source>Unsupported argument -socks found. Setting SOCKS version isn&apos;t possible anymore, only SOCKS5 proxies are supported.</source>
<translation type="unfinished"></translation>
</message>
@@ -4807,12 +4993,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+9"/>
<source>Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+13"/>
<source>%s is set very high!</source>
<translation type="unfinished"></translation>
</message>
@@ -4822,12 +5008,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+11"/>
<source>Always query for peer addresses via DNS lookup (default: %u)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+39"/>
<source>How many blocks to check at startup (default: %u, 0 = all)</source>
<translation type="unfinished"></translation>
</message>
@@ -4837,12 +5023,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Invalid -proxy address: &apos;%s&apos;</source>
- <translation>Invalid -proxy address: &apos;%s&apos;</translation>
- </message>
- <message>
- <location line="+7"/>
+ <location line="+14"/>
<source>Keypool ran out, please call keypoolrefill first</source>
<translation type="unfinished"></translation>
</message>
@@ -4977,27 +5158,27 @@
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="-80"/>
+ <location line="-81"/>
<source>Insufficient funds</source>
<translation>Insufficient funds</translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+15"/>
<source>Loading block index...</source>
<translation>Loading block index...</translation>
</message>
<message>
- <location line="-61"/>
+ <location line="-63"/>
<source>Add a node to connect to and attempt to keep the connection open</source>
<translation>Add a node to connect to and attempt to keep the connection open</translation>
</message>
<message>
- <location line="+62"/>
+ <location line="+64"/>
<source>Loading wallet...</source>
<translation>Loading wallet...</translation>
</message>
<message>
- <location line="-55"/>
+ <location line="-57"/>
<source>Cannot downgrade wallet</source>
<translation>Cannot downgrade wallet</translation>
</message>
@@ -5007,17 +5188,17 @@
<translation>Cannot write default address</translation>
</message>
<message>
- <location line="+78"/>
+ <location line="+81"/>
<source>Rescanning...</source>
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-67"/>
+ <location line="-70"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
<message>
- <location line="+15"/>
+ <location line="+16"/>
<source>Error</source>
<translation>Error</translation>
</message>
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 470fb6b377..4e88c8802c 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -11,7 +11,6 @@
#include "clientversion.h"
#include "streams.h"
-#include <boost/foreach.hpp>
RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 12d2d0f31c..27634eb179 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -166,6 +166,8 @@ void SendCoinsDialog::setModel(WalletModel *_model)
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
+ connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
updateFeeSectionControls();
updateMinFeeLabel();
@@ -652,7 +654,8 @@ void SendCoinsDialog::updateSmartFeeLabel()
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
FeeCalculation feeCalc;
- CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool);
+ bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, ui->optInRBF->isChecked());
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool, conservative_estimate);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
@@ -827,6 +830,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
} else {
CoinControlDialog::coinControl->nConfirmTarget = model->getDefaultConfirmTarget();
}
+ CoinControlDialog::coinControl->signalRbf = ui->optInRBF->isChecked();
for(int i = 0; i < ui->entries->count(); ++i)
{
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 10966e42eb..1b7cc69231 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -131,6 +131,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
move(QApplication::desktop()->screenGeometry().center() - r.center());
subscribeToCoreSignals();
+ installEventFilter(this);
}
SplashScreen::~SplashScreen()
@@ -138,6 +139,16 @@ SplashScreen::~SplashScreen()
unsubscribeFromCoreSignals();
}
+bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
+ if (ev->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ if(keyEvent->text()[0] == 'q' && breakAction != nullptr) {
+ breakAction();
+ }
+ }
+ return QObject::eventFilter(obj, ev);
+}
+
void SplashScreen::slotFinish(QWidget *mainWin)
{
Q_UNUSED(mainWin);
@@ -164,6 +175,18 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
InitMessage(splash, title + strprintf("%d", nProgress) + "%");
}
+void SplashScreen::setBreakAction(const std::function<void(void)> &action)
+{
+ breakAction = action;
+}
+
+static void SetProgressBreakAction(SplashScreen *splash, const std::function<void(void)> &action)
+{
+ QMetaObject::invokeMethod(splash, "setBreakAction",
+ Qt::QueuedConnection,
+ Q_ARG(std::function<void(void)>, action));
+}
+
#ifdef ENABLE_WALLET
void SplashScreen::ConnectWallet(CWallet* wallet)
{
@@ -177,6 +200,7 @@ void SplashScreen::subscribeToCoreSignals()
// Connect signals to client
uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1));
#ifdef ENABLE_WALLET
uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1));
#endif
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 95a65cc53c..a88ebb98a8 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_QT_SPLASHSCREEN_H
#define BITCOIN_QT_SPLASHSCREEN_H
+#include <functional>
#include <QSplashScreen>
class CWallet;
@@ -35,6 +36,11 @@ public Q_SLOTS:
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
+ /** Sets the break action */
+ void setBreakAction(const std::function<void(void)> &action);
+protected:
+ bool eventFilter(QObject * obj, QEvent * ev);
+
private:
/** Connect core signals to splash screen */
void subscribeToCoreSignals();
@@ -49,6 +55,8 @@ private:
int curAlignment;
QList<CWallet*> connectedWallets;
+
+ std::function<void(void)> breakAction;
};
#endif // BITCOIN_QT_SPLASHSCREEN_H
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 26dec3c610..fbad9e544a 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -12,6 +12,7 @@
#include "rpc/server.h"
#include "rpcconsole.h"
#include "test/testutil.h"
+#include "test/test_bitcoin.h"
#include "univalue.h"
#include "util.h"
@@ -35,8 +36,6 @@ void RPCNestedTests::rpcNestedTests()
{
// do some test setup
// could be moved to a more generic place when we add more tests on QT level
- const CChainParams& chainparams = Params();
- RegisterAllCoreRPCCommands(tableRPC);
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
ClearDatadirCache();
std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
@@ -44,15 +43,8 @@ void RPCNestedTests::rpcNestedTests()
dir.mkpath(".");
ForceSetArg("-datadir", path);
//mempool.setSanityCheck(1.0);
- pblocktree = new CBlockTreeDB(1 << 20, true);
- pcoinsdbview = new CCoinsViewDB(1 << 23, true);
- pcoinsTip = new CCoinsViewCache(pcoinsdbview);
- InitBlockIndex(chainparams);
- {
- CValidationState state;
- bool ok = ActivateBestChain(state, chainparams);
- QVERIFY(ok);
- }
+
+ TestingSetup test;
SetRPCWarmupFinished();
@@ -145,13 +137,5 @@ void RPCNestedTests::rpcNestedTests()
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
#endif
- UnloadBlockIndex();
- delete pcoinsTip;
- pcoinsTip = nullptr;
- delete pcoinsdbview;
- pcoinsdbview = nullptr;
- delete pblocktree;
- pblocktree = nullptr;
-
fs::remove_all(fs::path(path));
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index da070da084..03fd734e92 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -12,7 +12,6 @@
#include <stdint.h>
-#include <boost/foreach.hpp>
/* Return positive answer if transaction should be shown in list.
*/
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index e3e070b27f..43d6e8826b 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -336,6 +336,10 @@ void TransactionView::changedAmount(const QString &amount)
void TransactionView::exportClicked()
{
+ if (!model || !model->getOptionsModel()) {
+ return;
+ }
+
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Transaction History"), QString(),
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 6538a80233..60b55da3e7 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -19,6 +19,7 @@
#include "keystore.h"
#include "validation.h"
#include "net.h" // for g_connman
+#include "policy/fees.h"
#include "policy/rbf.h"
#include "sync.h"
#include "ui_interface.h"
@@ -34,7 +35,6 @@
#include <QSet>
#include <QTimer>
-#include <boost/foreach.hpp>
WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
@@ -668,7 +668,7 @@ bool WalletModel::bumpFee(uint256 hash)
std::unique_ptr<CFeeBumper> feeBump;
{
LOCK2(cs_main, wallet->cs_wallet);
- feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true));
+ feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true, FeeEstimateMode::UNSET));
}
if (feeBump->getResult() != BumpFeeResult::OK)
{
diff --git a/src/reverse_iterator.h b/src/reverse_iterator.h
new file mode 100644
index 0000000000..409f895ce0
--- /dev/null
+++ b/src/reverse_iterator.h
@@ -0,0 +1,39 @@
+// Taken from https://gist.github.com/arvidsson/7231973
+
+#ifndef BITCOIN_REVERSE_ITERATOR_HPP
+#define BITCOIN_REVERSE_ITERATOR_HPP
+
+/**
+ * Template used for reverse iteration in C++11 range-based for loops.
+ *
+ * std::vector<int> v = {1, 2, 3, 4, 5};
+ * for (auto x : reverse_iterate(v))
+ * std::cout << x << " ";
+ */
+
+template <typename T>
+class reverse_range
+{
+ T &x;
+
+public:
+ reverse_range(T &x) : x(x) {}
+
+ auto begin() const -> decltype(this->x.rbegin())
+ {
+ return x.rbegin();
+ }
+
+ auto end() const -> decltype(this->x.rend())
+ {
+ return x.rend();
+ }
+};
+
+template <typename T>
+reverse_range<T> reverse_iterate(T &x)
+{
+ return reverse_range<T>(x);
+}
+
+#endif // BITCOIN_REVERSE_ITERATOR_HPP
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 8f7f76841d..c17ca2fa3a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -991,7 +991,7 @@ UniValue gettxout(const JSONRPCRequest& request)
if (fMempool) {
LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip, mempool);
- if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { // TODO: filtering spent coins should be done by the CCoinsViewMemPool
+ if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return NullUniValue;
}
} else {
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index cb1539dce5..278c4ecc2d 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -37,6 +37,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getnetworkhashps", 1, "height" },
{ "sendtoaddress", 1, "amount" },
{ "sendtoaddress", 4, "subtractfeefromamount" },
+ { "sendtoaddress", 5 , "replaceable" },
+ { "sendtoaddress", 6 , "conf_target" },
{ "settxfee", 0, "amount" },
{ "getreceivedbyaddress", 1, "minconf" },
{ "getreceivedbyaccount", 1, "minconf" },
@@ -69,6 +71,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 1, "amounts" },
{ "sendmany", 2, "minconf" },
{ "sendmany", 4, "subtractfeefrom" },
+ { "sendmany", 5 , "replaceable" },
+ { "sendmany", 6 , "conf_target" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "createmultisig", 0, "nrequired" },
@@ -79,6 +83,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listunspent", 3, "include_unsafe" },
{ "listunspent", 4, "query_options" },
{ "getblock", 1, "verbosity" },
+ { "getblock", 1, "verbose" },
{ "getblockheader", 1, "verbose" },
{ "getchaintxstats", 0, "nblocks" },
{ "gettransaction", 1, "include_watchonly" },
@@ -86,7 +91,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" },
{ "createrawtransaction", 2, "locktime" },
- { "createrawtransaction", 3, "optintorbf" },
+ { "createrawtransaction", 3, "replaceable" },
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
{ "sendrawtransaction", 1, "allowhighfees" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 8c682592c5..e50742f36e 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -18,6 +18,7 @@
#include "policy/fees.h"
#include "pow.h"
#include "rpc/blockchain.h"
+#include "rpc/mining.h"
#include "rpc/server.h"
#include "txmempool.h"
#include "util.h"
@@ -141,42 +142,6 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
return blockHashes;
}
-UniValue generate(const JSONRPCRequest& request)
-{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- "generate nblocks ( maxtries )\n"
- "\nMine up to nblocks blocks immediately (before the RPC call returns)\n"
- "\nArguments:\n"
- "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
- "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
- "\nResult:\n"
- "[ blockhashes ] (array) hashes of blocks generated\n"
- "\nExamples:\n"
- "\nGenerate 11 blocks\n"
- + HelpExampleCli("generate", "11")
- );
-
- int nGenerate = request.params[0].get_int();
- uint64_t nMaxTries = 1000000;
- if (request.params.size() > 1) {
- nMaxTries = request.params[1].get_int();
- }
-
- std::shared_ptr<CReserveScript> coinbaseScript;
- GetMainSignals().ScriptForMining(coinbaseScript);
-
- // If the keypool is exhausted, no script is returned at all. Catch this.
- if (!coinbaseScript)
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
-
- //throw an error if no script was provided
- if (coinbaseScript->reserveScript.empty())
- throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
-
- return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true);
-}
-
UniValue generatetoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
@@ -962,7 +927,6 @@ static const CRPCCommand commands[] =
{ "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} },
{ "mining", "submitblock", &submitblock, true, {"hexdata","dummy"} },
- { "generating", "generate", &generate, true, {"nblocks","maxtries"} },
{ "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} },
{ "util", "estimatefee", &estimatefee, true, {"nblocks"} },
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
new file mode 100644
index 0000000000..a148d851da
--- /dev/null
+++ b/src/rpc/mining.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_RPC_MINING_H
+#define BITCOIN_RPC_MINING_H
+
+#include "script/script.h"
+
+#include <univalue.h>
+
+/** Generate blocks (mine) */
+UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
+
+#endif
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index ef19e481c2..fcbbe1ceed 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -174,6 +174,14 @@ UniValue validateaddress(const JSONRPCRequest& request)
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
+ " \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n"
+ " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n"
+ " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n"
+ " [\n"
+ " \"address\"\n"
+ " ,...\n"
+ " ]\n"
+ " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n"
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 5cab0ad5bd..ed452fcb02 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -19,7 +19,6 @@
#include "utilstrencodings.h"
#include "version.h"
-#include <boost/foreach.hpp>
#include <univalue.h>
@@ -302,9 +301,8 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request)
" ,...\n"
"]\n"
"\nExamples:\n"
- + HelpExampleCli("getaddednodeinfo", "true")
- + HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"")
- + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"")
+ + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
+ + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
);
if(!g_connman)
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 527a4d6974..b878624df8 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -294,7 +294,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( optintorbf )\n"
+ "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"
@@ -318,7 +318,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
" ,...\n"
" }\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. optintorbf (boolean, optional, default=false) Allow 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. 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"
"\"transaction\" (string) hex string of the transaction\n"
@@ -412,7 +413,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict optintorbf option");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
return EncodeHexTx(rawTx);
@@ -900,7 +901,7 @@ static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} },
- { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime"} },
+ { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} },
{ "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} },
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index c320d20453..63e4e9c630 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -17,7 +17,6 @@
#include <univalue.h>
#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
#include <boost/algorithm/string/classification.hpp>
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 923ba2c231..36a6d5110d 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -139,3 +139,69 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
}
return result;
}
+
+bool CScheduler::AreThreadsServicingQueue() const {
+ return nThreadsServicingQueue;
+}
+
+
+void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
+ {
+ LOCK(m_cs_callbacks_pending);
+ // Try to avoid scheduling too many copies here, but if we
+ // accidentally have two ProcessQueue's scheduled at once its
+ // not a big deal.
+ if (m_are_callbacks_running) return;
+ if (m_callbacks_pending.empty()) return;
+ }
+ m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this));
+}
+
+void SingleThreadedSchedulerClient::ProcessQueue() {
+ std::function<void (void)> callback;
+ {
+ LOCK(m_cs_callbacks_pending);
+ if (m_are_callbacks_running) return;
+ if (m_callbacks_pending.empty()) return;
+ m_are_callbacks_running = true;
+
+ callback = std::move(m_callbacks_pending.front());
+ m_callbacks_pending.pop_front();
+ }
+
+ // RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue
+ // to ensure both happen safely even if callback() throws.
+ struct RAIICallbacksRunning {
+ SingleThreadedSchedulerClient* instance;
+ RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
+ ~RAIICallbacksRunning() {
+ {
+ LOCK(instance->m_cs_callbacks_pending);
+ instance->m_are_callbacks_running = false;
+ }
+ instance->MaybeScheduleProcessQueue();
+ }
+ } raiicallbacksrunning(this);
+
+ callback();
+}
+
+void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void (void)> func) {
+ assert(m_pscheduler);
+
+ {
+ LOCK(m_cs_callbacks_pending);
+ m_callbacks_pending.emplace_back(std::move(func));
+ }
+ MaybeScheduleProcessQueue();
+}
+
+void SingleThreadedSchedulerClient::EmptyQueue() {
+ assert(!m_pscheduler->AreThreadsServicingQueue());
+ bool should_continue = true;
+ while (should_continue) {
+ ProcessQueue();
+ LOCK(m_cs_callbacks_pending);
+ should_continue = !m_callbacks_pending.empty();
+ }
+}
diff --git a/src/scheduler.h b/src/scheduler.h
index 27412a15b4..0365d668b2 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -14,6 +14,8 @@
#include <boost/thread.hpp>
#include <map>
+#include "sync.h"
+
//
// Simple class for background tasks that should be run
// periodically or once "after a while"
@@ -41,7 +43,7 @@ public:
typedef std::function<void(void)> Function;
// Call func at/after time t
- void schedule(Function f, boost::chrono::system_clock::time_point t);
+ void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());
// Convenience method: call f once deltaSeconds from now
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);
@@ -69,6 +71,9 @@ public:
size_t getQueueInfo(boost::chrono::system_clock::time_point &first,
boost::chrono::system_clock::time_point &last) const;
+ // Returns true if there are threads actively running in serviceQueue()
+ bool AreThreadsServicingQueue() const;
+
private:
std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue;
boost::condition_variable newTaskScheduled;
@@ -79,4 +84,30 @@ private:
bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }
};
+/**
+ * Class used by CScheduler clients which may schedule multiple jobs
+ * which are required to be run serially. Does not require such jobs
+ * to be executed on the same thread, but no two jobs will be executed
+ * at the same time.
+ */
+class SingleThreadedSchedulerClient {
+private:
+ CScheduler *m_pscheduler;
+
+ CCriticalSection m_cs_callbacks_pending;
+ std::list<std::function<void (void)>> m_callbacks_pending;
+ bool m_are_callbacks_running = false;
+
+ void MaybeScheduleProcessQueue();
+ void ProcessQueue();
+
+public:
+ SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {}
+ void AddToProcessQueue(std::function<void (void)> func);
+
+ // Processes all remaining queue members on the calling thread, blocking until queue is empty
+ // Must be called after the CScheduler has no remaining processing threads!
+ void EmptyQueue();
+};
+
#endif
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 60f6f711e6..ab1dc4e681 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -160,9 +160,9 @@ protected:
public:
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {}
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
- bool CheckLockTime(const CScriptNum& nLockTime) const;
- bool CheckSequence(const CScriptNum& nSequence) const;
+ bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
+ bool CheckLockTime(const CScriptNum& nLockTime) const override;
+ bool CheckSequence(const CScriptNum& nSequence) const override;
};
class MutableTransactionSignatureChecker : public TransactionSignatureChecker
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 35b534344e..0a39619734 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -11,7 +11,6 @@
#include "script/standard.h"
#include "script/sign.h"
-#include <boost/foreach.hpp>
typedef std::vector<unsigned char> valtype;
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index befc5f5233..ceb573b2ec 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -74,10 +74,10 @@ void InitSignatureCache()
{
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
- LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n",
- (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
+ LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
+ (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}
bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 55cec4cc8d..5832b264b3 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -48,7 +48,7 @@ private:
public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
- bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
+ bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
};
void InitSignatureCache();
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 4b01a6de94..dc50467d3f 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -12,7 +12,6 @@
#include "script/standard.h"
#include "uint256.h"
-#include <boost/foreach.hpp>
typedef std::vector<unsigned char> valtype;
@@ -393,7 +392,7 @@ class DummySignatureChecker : public BaseSignatureChecker
public:
DummySignatureChecker() {}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+ bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
{
return true;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index f3c0be4139..bd45862892 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -40,8 +40,8 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
public:
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
- const BaseSignatureChecker& Checker() const { return checker; }
- bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const;
+ const BaseSignatureChecker& Checker() const override { return checker; }
+ bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
@@ -55,8 +55,8 @@ public:
class DummySignatureCreator : public BaseSignatureCreator {
public:
DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {}
- const BaseSignatureChecker& Checker() const;
- bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const;
+ const BaseSignatureChecker& Checker() const override;
+ bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
struct SignatureData {
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 8e08acf0c6..760a5305e5 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -10,7 +10,6 @@
#include "util.h"
#include "utilstrencodings.h"
-#include <boost/foreach.hpp>
typedef std::vector<unsigned char> valtype;
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 98c1581093..2df6b84a59 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -148,9 +148,9 @@ class Win32LockedPageAllocator: public LockedPageAllocator
{
public:
Win32LockedPageAllocator();
- void* AllocateLocked(size_t len, bool *lockingSuccess);
- void FreeLocked(void* addr, size_t len);
- size_t GetLimit();
+ void* AllocateLocked(size_t len, bool *lockingSuccess) override;
+ void FreeLocked(void* addr, size_t len) override;
+ size_t GetLimit() override;
private:
size_t page_size;
};
@@ -200,9 +200,9 @@ class PosixLockedPageAllocator: public LockedPageAllocator
{
public:
PosixLockedPageAllocator();
- void* AllocateLocked(size_t len, bool *lockingSuccess);
- void FreeLocked(void* addr, size_t len);
- size_t GetLimit();
+ void* AllocateLocked(size_t len, bool *lockingSuccess) override;
+ void FreeLocked(void* addr, size_t len) override;
+ size_t GetLimit() override;
private:
size_t page_size;
};
diff --git a/src/sync.cpp b/src/sync.cpp
index 94f2cafa98..c359e8220b 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -9,7 +9,6 @@
#include <stdio.h>
-#include <boost/foreach.hpp>
#include <boost/thread.hpp>
#ifdef DEBUG_LOCKCONTENTION
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index dc5372a070..bc6aef2c11 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -27,7 +27,7 @@ public:
insecure_rand = FastRandomContext(true);
}
- int RandomInt(int nMax)
+ int RandomInt(int nMax) override
{
state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash();
return (unsigned int)(state % nMax);
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index 3f15a0dec1..4a533b5bf2 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -131,7 +131,7 @@ class TestLockedPageAllocator: public LockedPageAllocator
{
public:
TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {}
- void* AllocateLocked(size_t len, bool *lockingSuccess)
+ void* AllocateLocked(size_t len, bool *lockingSuccess) override
{
*lockingSuccess = false;
if (count > 0) {
@@ -146,10 +146,10 @@ public:
}
return 0;
}
- void FreeLocked(void* addr, size_t len)
+ void FreeLocked(void* addr, size_t len) override
{
}
- size_t GetLimit()
+ size_t GetLimit() override
{
return std::numeric_limits<size_t>::max();
}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 01cc5ed831..c79675f5a6 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -9,7 +9,6 @@
#include <vector>
#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 6d8aab887b..c686f679c2 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -14,7 +14,6 @@
#include "test/test_bitcoin.h"
-#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
typedef std::vector<unsigned char> valtype;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 66354699b2..095d86834c 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -29,7 +29,7 @@ public:
class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
- void Serialize(CDataStream& s) const
+ void Serialize(CDataStream& s) const override
{
CAddrMan::Serialize(s);
}
@@ -38,7 +38,7 @@ public:
class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
- void Serialize(CDataStream& s) const
+ void Serialize(CDataStream& s) const override
{
// Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
unsigned char nVersion = 1;
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 6bfd315647..8cdd392109 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -184,8 +184,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.TrimToSize(1);
BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
for (int i = 1; i < 10; i++) {
- BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK());
- BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
}
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 11bb11d1e9..345c4a2148 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -5,6 +5,7 @@
#include <vector>
#include "prevector.h"
+#include "reverse_iterator.h"
#include "serialize.h"
#include "streams.h"
@@ -56,13 +57,13 @@ class prevector_tester {
for (const T& v : pre_vector) {
local_check(v == real_vector[pos++]);
}
- BOOST_REVERSE_FOREACH(const T& v, pre_vector) {
+ for (const T& v : reverse_iterate(pre_vector)) {
local_check(v == real_vector[--pos]);
}
for (const T& v : const_pre_vector) {
local_check(v == real_vector[pos++]);
}
- BOOST_REVERSE_FOREACH(const T& v, const_pre_vector) {
+ for (const T& v : reverse_iterate(const_pre_vector)) {
local_check(v == real_vector[--pos]);
}
CDataStream ss1(SER_DISK, 0);
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 1bb191c73d..a18471588a 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -24,7 +24,6 @@
#include <string>
#include <vector>
-#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index c60379982e..3ba81ed17b 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -38,6 +38,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
+ InitScriptExecutionCache();
fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(chainName);
@@ -61,6 +62,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000)));
fs::create_directories(pathTemp);
ForceSetArg("-datadir", pathTemp.string());
+
+ // Note that because we don't bother running a scheduler thread here,
+ // callbacks via CValidationInterface are unreliable, but that's OK,
+ // our unit tests aren't testing multiple parts of the code at once.
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+
mempool.setSanityCheck(1.0);
pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
@@ -87,6 +94,8 @@ TestingSetup::~TestingSetup()
UnregisterNodeSignals(GetNodeSignals());
threadGroup.interrupt_all();
threadGroup.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index c9e4a3427f..dd3b13c8c8 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -10,6 +10,7 @@
#include "key.h"
#include "pubkey.h"
#include "random.h"
+#include "scheduler.h"
#include "txdb.h"
#include "txmempool.h"
@@ -53,6 +54,7 @@ struct TestingSetup: public BasicTestingSetup {
fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
+ CScheduler scheduler;
TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 778d2fd742..39f9f58604 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -27,7 +27,6 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/test/unit_test.hpp>
-#include <boost/foreach.hpp>
#include <univalue.h>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index c5367208ba..a74f40251a 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -10,11 +10,17 @@
#include "txmempool.h"
#include "random.h"
#include "script/standard.h"
+#include "script/sign.h"
#include "test/test_bitcoin.h"
#include "utiltime.h"
+#include "core_io.h"
+#include "keystore.h"
+#include "policy/policy.h"
#include <boost/test/unit_test.hpp>
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
+
BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
static bool
@@ -84,4 +90,282 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
BOOST_CHECK_EQUAL(mempool.size(), 0);
}
+// Run CheckInputs (using pcoinsTip) on the given transaction, for all script
+// flags. Test that CheckInputs passes for all flags that don't overlap with
+// the failing_flags argument, but otherwise fails.
+// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
+// get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if
+// the script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain
+// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain
+// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution
+// should fail.
+// Capture this interaction with the upgraded_nop argument: set it when evaluating
+// any script flag that is implemented as an upgraded NOP code.
+void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache, bool upgraded_nop)
+{
+ PrecomputedTransactionData txdata(tx);
+ // If we add many more flags, this loop can get too expensive, but we can
+ // rewrite in the future to randomly pick a set of flags to evaluate.
+ for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) {
+ CValidationState state;
+ // Filter out incompatible flag choices
+ if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
+ // CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in
+ // script/interpreter.cpp
+ test_flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS;
+ }
+ if ((test_flags & SCRIPT_VERIFY_WITNESS)) {
+ // WITNESS requires P2SH
+ test_flags |= SCRIPT_VERIFY_P2SH;
+ }
+ bool ret = CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, nullptr);
+ // CheckInputs should succeed iff test_flags doesn't intersect with
+ // failing_flags
+ bool expected_return_value = !(test_flags & failing_flags);
+ if (expected_return_value && upgraded_nop) {
+ // If the script flag being tested corresponds to an upgraded NOP,
+ // then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS
+ // is set.
+ expected_return_value = !(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS);
+ }
+ BOOST_CHECK_EQUAL(ret, expected_return_value);
+
+ // Test the caching
+ if (ret && add_to_cache) {
+ // Check that we get a cache hit if the tx was valid
+ std::vector<CScriptCheck> scriptchecks;
+ BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(scriptchecks.empty());
+ } else {
+ // Check that we get script executions to check, if the transaction
+ // was invalid, or we didn't add to cache.
+ std::vector<CScriptCheck> scriptchecks;
+ BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
+ }
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
+{
+ // Test that passing CheckInputs with one set of script flags doesn't imply
+ // that we would pass again with a different set of flags.
+ InitScriptExecutionCache();
+
+ CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
+ CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
+
+ CBasicKeyStore keystore;
+ keystore.AddKey(coinbaseKey);
+ keystore.AddCScript(p2pk_scriptPubKey);
+
+ // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing
+
+ // Create 2 outputs that match the three scripts above, spending the first
+ // coinbase tx.
+ CMutableTransaction spend_tx;
+
+ spend_tx.nVersion = 1;
+ spend_tx.vin.resize(1);
+ spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash();
+ spend_tx.vin[0].prevout.n = 0;
+ spend_tx.vout.resize(4);
+ spend_tx.vout[0].nValue = 11*CENT;
+ spend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey;
+ spend_tx.vout[1].nValue = 11*CENT;
+ spend_tx.vout[1].scriptPubKey = p2wpkh_scriptPubKey;
+ spend_tx.vout[2].nValue = 11*CENT;
+ spend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ spend_tx.vout[3].nValue = 11*CENT;
+ spend_tx.vout[3].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+
+ // Sign, with a non-DER signature
+ {
+ std::vector<unsigned char> vchSig;
+ uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER
+ vchSig.push_back((unsigned char)SIGHASH_ALL);
+ spend_tx.vin[0].scriptSig << vchSig;
+ }
+
+ LOCK(cs_main);
+
+ // Test that invalidity under a set of flags doesn't preclude validity
+ // under other (eg consensus) flags.
+ // spend_tx is invalid according to DERSIG
+ CValidationState state;
+ {
+ PrecomputedTransactionData ptd_spend_tx(spend_tx);
+
+ BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, 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, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK_EQUAL(scriptchecks.size(), 1);
+
+ // 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, false);
+
+ // And if we produce a block with this tx, it should be valid (DERSIG not
+ // enabled yet), even though there's no cache entry.
+ CBlock block;
+
+ block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
+ BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
+ }
+
+ // Test P2SH: construct a transaction that is valid without P2SH, and
+ // then test validity with P2SH.
+ {
+ CMutableTransaction invalid_under_p2sh_tx;
+ invalid_under_p2sh_tx.nVersion = 1;
+ invalid_under_p2sh_tx.vin.resize(1);
+ invalid_under_p2sh_tx.vin[0].prevout.hash = spend_tx.GetHash();
+ invalid_under_p2sh_tx.vin[0].prevout.n = 0;
+ invalid_under_p2sh_tx.vout.resize(1);
+ invalid_under_p2sh_tx.vout[0].nValue = 11*CENT;
+ invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
+ 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, false);
+ }
+
+ // Test CHECKLOCKTIMEVERIFY
+ {
+ CMutableTransaction invalid_with_cltv_tx;
+ invalid_with_cltv_tx.nVersion = 1;
+ invalid_with_cltv_tx.nLockTime = 100;
+ invalid_with_cltv_tx.vin.resize(1);
+ invalid_with_cltv_tx.vin[0].prevout.hash = spend_tx.GetHash();
+ invalid_with_cltv_tx.vin[0].prevout.n = 2;
+ invalid_with_cltv_tx.vin[0].nSequence = 0;
+ invalid_with_cltv_tx.vout.resize(1);
+ invalid_with_cltv_tx.vout[0].nValue = 11*CENT;
+ invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
+
+ // Sign
+ std::vector<unsigned char> vchSig;
+ uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ 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, 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, true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ }
+
+ // TEST CHECKSEQUENCEVERIFY
+ {
+ CMutableTransaction invalid_with_csv_tx;
+ invalid_with_csv_tx.nVersion = 2;
+ invalid_with_csv_tx.vin.resize(1);
+ invalid_with_csv_tx.vin[0].prevout.hash = spend_tx.GetHash();
+ invalid_with_csv_tx.vin[0].prevout.n = 3;
+ invalid_with_csv_tx.vin[0].nSequence = 100;
+ invalid_with_csv_tx.vout.resize(1);
+ invalid_with_csv_tx.vout[0].nValue = 11*CENT;
+ invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
+
+ // Sign
+ std::vector<unsigned char> vchSig;
+ uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ 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, 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, true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ }
+
+ // TODO: add tests for remaining script flags
+
+ // Test that passing CheckInputs with a valid witness doesn't imply success
+ // for the same tx with a different witness.
+ {
+ CMutableTransaction valid_with_witness_tx;
+ valid_with_witness_tx.nVersion = 1;
+ valid_with_witness_tx.vin.resize(1);
+ valid_with_witness_tx.vin[0].prevout.hash = spend_tx.GetHash();
+ valid_with_witness_tx.vin[0].prevout.n = 1;
+ valid_with_witness_tx.vout.resize(1);
+ valid_with_witness_tx.vout[0].nValue = 11*CENT;
+ valid_with_witness_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
+
+ // Sign
+ SignatureData sigdata;
+ ProduceSignature(MutableTransactionSignatureCreator(&keystore, &valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata);
+ UpdateTransaction(valid_with_witness_tx, 0, sigdata);
+
+ // This should be valid under all script flags.
+ ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true, false);
+
+ // 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, false);
+ }
+
+ {
+ // Test a transaction with multiple inputs.
+ CMutableTransaction tx;
+
+ tx.nVersion = 1;
+ tx.vin.resize(2);
+ tx.vin[0].prevout.hash = spend_tx.GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vin[1].prevout.hash = spend_tx.GetHash();
+ tx.vin[1].prevout.n = 1;
+ tx.vout.resize(1);
+ tx.vout[0].nValue = 22*CENT;
+ tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
+
+ // Sign
+ for (int i=0; i<2; ++i) {
+ SignatureData sigdata;
+ ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata);
+ UpdateTransaction(tx, i, sigdata);
+ }
+
+ // This should be valid under all script flags
+ ValidateCheckInputsForAllFlags(tx, 0, true, false);
+
+ // Check that if the second input is invalid, but the first input is
+ // valid, the transaction is not cached.
+ // Invalidate vin[1]
+ tx.vin[1].scriptWitness.SetNull();
+
+ CValidationState state;
+ PrecomputedTransactionData txdata(tx);
+ // This transaction is now invalid under segwit, because of the second input.
+ BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip, 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, 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(), 2);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index faa2383d14..722f6ae059 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -22,11 +22,11 @@ private:
mutable ThresholdConditionCache cache;
public:
- int64_t BeginTime(const Consensus::Params& params) const { return TestTime(10000); }
- int64_t EndTime(const Consensus::Params& params) const { return TestTime(20000); }
- int Period(const Consensus::Params& params) const { return 1000; }
- int Threshold(const Consensus::Params& params) const { return 900; }
- bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); }
+ int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); }
+ int64_t EndTime(const Consensus::Params& params) const override { return TestTime(20000); }
+ int Period(const Consensus::Params& params) const override { return 1000; }
+ int Threshold(const Consensus::Params& params) const override { return 900; }
+ bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (pindex->nVersion & 0x100); }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
diff --git a/src/timedata.cpp b/src/timedata.cpp
index d736baa213..099ed7f042 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -15,7 +15,6 @@
#include "utilstrencodings.h"
#include "warnings.h"
-#include <boost/foreach.hpp>
static CCriticalSection cs_nTimeOffset;
static int64_t nTimeOffset = 0;
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 4cd64bf028..3665e7e770 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -17,7 +17,6 @@
#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
-#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 97e916fd22..002f6550bc 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -7,8 +7,12 @@
#include "chainparams.h"
#include "hash.h"
+#include "random.h"
#include "pow.h"
#include "uint256.h"
+#include "util.h"
+#include "ui_interface.h"
+#include "init.h"
#include <stdint.h>
@@ -21,6 +25,7 @@ static const char DB_TXINDEX = 't';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_BEST_BLOCK = 'B';
+static const char DB_HEAD_BLOCKS = 'H';
static const char DB_FLAG = 'F';
static const char DB_REINDEX_FLAG = 'R';
static const char DB_LAST_BLOCK = 'l';
@@ -68,10 +73,39 @@ uint256 CCoinsViewDB::GetBestBlock() const {
return hashBestChain;
}
+std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
+ std::vector<uint256> vhashHeadBlocks;
+ if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
+ return std::vector<uint256>();
+ }
+ return vhashHeadBlocks;
+}
+
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
CDBBatch batch(db);
size_t count = 0;
size_t changed = 0;
+ size_t batch_size = (size_t)GetArg("-dbbatchsize", nDefaultDbBatchSize);
+ int crash_simulate = GetArg("-dbcrashratio", 0);
+ assert(!hashBlock.IsNull());
+
+ uint256 old_tip = GetBestBlock();
+ if (old_tip.IsNull()) {
+ // We may be in the middle of replaying.
+ std::vector<uint256> old_heads = GetHeadBlocks();
+ if (old_heads.size() == 2) {
+ assert(old_heads[0] == hashBlock);
+ old_tip = old_heads[1];
+ }
+ }
+
+ // In the first batch, mark the database as being in the middle of a
+ // transition from old_tip to hashBlock.
+ // A vector is used for future extensibility, as we may want to support
+ // interrupting after partial writes from multiple independent reorgs.
+ batch.Erase(DB_BEST_BLOCK);
+ batch.Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip});
+
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
CoinEntry entry(&it->first);
@@ -84,10 +118,25 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
count++;
CCoinsMap::iterator itOld = it++;
mapCoins.erase(itOld);
+ if (batch.SizeEstimate() > batch_size) {
+ LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
+ db.WriteBatch(batch);
+ batch.Clear();
+ if (crash_simulate) {
+ static FastRandomContext rng;
+ if (rng.randrange(crash_simulate) == 0) {
+ LogPrintf("Simulating a crash. Goodbye.\n");
+ _Exit(0);
+ }
+ }
+ }
}
- if (!hashBlock.IsNull())
- batch.Write(DB_BEST_BLOCK, hashBlock);
+ // In the last batch, mark the database as consistent with hashBlock again.
+ batch.Erase(DB_HEAD_BLOCKS);
+ batch.Write(DB_BEST_BLOCK, hashBlock);
+
+ LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
bool ret = db.WriteBatch(batch);
LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
return ret;
@@ -319,13 +368,30 @@ bool CCoinsViewDB::Upgrade() {
return true;
}
- LogPrintf("Upgrading database...\n");
+ int64_t count = 0;
+ LogPrintf("Upgrading utxo-set database...\n");
+ LogPrintf("[0%%]...");
size_t batch_size = 1 << 24;
CDBBatch batch(db);
+ uiInterface.SetProgressBreakAction(StartShutdown);
+ int reportDone = 0;
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
+ if (ShutdownRequested()) {
+ break;
+ }
std::pair<unsigned char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_COINS) {
+ if (count++ % 256 == 0) {
+ uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
+ int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
+ uiInterface.ShowProgress(_("Upgrading UTXO database") + "\n"+ _("(press q to shutdown and continue later)") + "\n", percentageDone);
+ if (reportDone < percentageDone/10) {
+ // report max. every 10% step
+ LogPrintf("[%d%%]...", percentageDone);
+ reportDone = percentageDone/10;
+ }
+ }
CCoins old_coins;
if (!pcursor->GetValue(old_coins)) {
return error("%s: cannot parse CCoins record", __func__);
@@ -350,5 +416,7 @@ bool CCoinsViewDB::Upgrade() {
}
}
db.WriteBatch(batch);
+ uiInterface.SetProgressBreakAction(std::function<void(void)>());
+ LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE");
return true;
}
diff --git a/src/txdb.h b/src/txdb.h
index 2a3e4eb696..adcbc73380 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -19,12 +19,12 @@ class CBlockIndex;
class CCoinsViewDBCursor;
class uint256;
-//! Compensate for extra memory peak (x1.5-x1.9) at flush time.
-static constexpr int DB_PEAK_USAGE_FACTOR = 2;
//! No need to periodic flush if at least this much space still available.
-static constexpr int MAX_BLOCK_COINSDB_USAGE = 10 * DB_PEAK_USAGE_FACTOR;
+static constexpr int MAX_BLOCK_COINSDB_USAGE = 10;
//! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 450;
+//! -dbbatchsize default (bytes)
+static const int64_t nDefaultDbBatchSize = 16 << 20;
//! max. -dbcache (MiB)
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB)
@@ -74,6 +74,7 @@ public:
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
+ std::vector<uint256> GetHeadBlocks() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
CCoinsViewCursor *Cursor() const override;
@@ -88,12 +89,12 @@ class CCoinsViewDBCursor: public CCoinsViewCursor
public:
~CCoinsViewDBCursor() {}
- bool GetKey(COutPoint &key) const;
- bool GetValue(Coin &coin) const;
- unsigned int GetValueSize() const;
+ bool GetKey(COutPoint &key) const override;
+ bool GetValue(Coin &coin) const override;
+ unsigned int GetValueSize() const override;
- bool Valid() const;
- void Next();
+ bool Valid() const override;
+ void Next() override;
private:
CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256 &hashBlockIn):
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index dcfc5ffde0..4a81055231 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -11,6 +11,7 @@
#include "validation.h"
#include "policy/policy.h"
#include "policy/fees.h"
+#include "reverse_iterator.h"
#include "streams.h"
#include "timedata.h"
#include "util.h"
@@ -127,7 +128,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// This maximizes the benefit of the descendant cache and guarantees that
// setMemPoolChildren will be updated, an assumption made in
// UpdateForDescendants.
- BOOST_REVERSE_FOREACH(const uint256 &hash, vHashesToUpdate) {
+ for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) {
// we cache the in-mempool children to avoid duplicate updates
setEntries setChildren;
// calculate children from mapNextTx
diff --git a/src/txmempool.h b/src/txmempool.h
index 78ac3c209b..d272114a7c 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -618,13 +618,6 @@ public:
return (mapTx.count(hash) != 0);
}
- bool exists(const COutPoint& outpoint) const
- {
- LOCK(cs);
- auto it = mapTx.find(outpoint.hash);
- return (it != mapTx.end() && outpoint.n < it->GetTx().vout.size());
- }
-
CTransactionRef get(const uint256& hash) const;
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
@@ -676,6 +669,13 @@ private:
/**
* CCoinsView that brings transactions from a memorypool into view.
* It does not check for spendings by memory pool transactions.
+ * Instead, it provides access to all Coins which are either unspent in the
+ * base CCoinsView, or are outputs from any mempool transaction!
+ * This allows transaction replacement to work as expected, as you want to
+ * have all inputs "available" to check signatures, and any cycles in the
+ * dependency graph are checked directly in AcceptToMemoryPool.
+ * It also allows you to sign a double-spend directly in signrawtransaction,
+ * as long as the conflicting transaction is not yet confirmed.
*/
class CCoinsViewMemPool : public CCoinsViewBacked
{
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 090402aeed..762dd19b19 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -97,6 +97,9 @@ public:
/** Show progress e.g. for verifychain */
boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;
+ /** Set progress break action (possible "cancel button" triggers that action) */
+ boost::signals2::signal<void (std::function<void(void)> action)> SetProgressBreakAction;
+
/** New block has been accepted */
boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip;
diff --git a/src/util.cpp b/src/util.cpp
index 0e1464e590..b76c173f90 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -422,7 +422,9 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[])
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg)
{
LOCK(cs_args);
- return mapMultiArgs.at(strArg);
+ if (IsArgSet(strArg))
+ return mapMultiArgs.at(strArg);
+ return {};
}
bool ArgsManager::IsArgSet(const std::string& strArg)
diff --git a/src/validation.cpp b/src/validation.cpp
index eb6ea42b63..09288be1ca 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -14,6 +14,7 @@
#include "consensus/merkle.h"
#include "consensus/tx_verify.h"
#include "consensus/validation.h"
+#include "cuckoocache.h"
#include "fs.h"
#include "hash.h"
#include "init.h"
@@ -23,6 +24,7 @@
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "random.h"
+#include "reverse_iterator.h"
#include "script/script.h"
#include "script/sigcache.h"
#include "script/standard.h"
@@ -96,7 +98,7 @@ namespace {
struct CBlockIndexWorkComparator
{
- bool operator()(CBlockIndex *pa, CBlockIndex *pb) const {
+ bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
// First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true;
@@ -189,7 +191,7 @@ enum FlushStateMode {
static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
-static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool CheckFinalTx(const CTransaction &tx, int flags)
@@ -312,6 +314,9 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
return EvaluateSequenceLocks(index, lockPair);
}
+// Returns the script flags which should be checked for a given block
+static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
+
static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
@@ -395,6 +400,42 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
+// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool
+// were somehow broken and returning the wrong scriptPubKeys
+static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, CTxMemPool& pool,
+ unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) {
+ AssertLockHeld(cs_main);
+
+ // pool.cs should be locked already, but go ahead and re-take the lock here
+ // to enforce that mempool doesn't change between when we check the view
+ // and when we actually call through to CheckInputs
+ LOCK(pool.cs);
+
+ assert(!tx.IsCoinBase());
+ for (const CTxIn& txin : tx.vin) {
+ const Coin& coin = view.AccessCoin(txin.prevout);
+
+ // At this point we haven't actually checked if the coins are all
+ // available (or shouldn't assume we have, since CheckInputs does).
+ // So we just return failure if the inputs are not available here,
+ // and then only have to check equivalence for available inputs.
+ if (coin.IsSpent()) return false;
+
+ const CTransactionRef& txFrom = pool.get(txin.prevout.hash);
+ if (txFrom) {
+ assert(txFrom->GetHash() == txin.prevout.hash);
+ assert(txFrom->vout.size() > txin.prevout.n);
+ assert(txFrom->vout[txin.prevout.n] == coin.out);
+ } else {
+ const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout);
+ assert(!coinFromDisk.IsSpent());
+ assert(coinFromDisk.out == coin.out);
+ }
+ }
+
+ return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
+}
+
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
@@ -751,32 +792,51 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata(tx);
- if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) {
+ if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) {
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
CValidationState stateDummy; // Want reported failures to be from first CheckInputs
- if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
- !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
+ if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
+ !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
state.SetCorruptionPossible();
}
return false; // state filled in by CheckInputs
}
- // Check again against just the consensus-critical mandatory script
- // verification flags, in case of bugs in the standard flags that cause
+ // Check again against the current block tip's script verification
+ // flags to cache our script execution flags. This is, of course,
+ // useless if the next block has different script flags from the
+ // previous one, but because the cache tracks script flags for us it
+ // will auto-invalidate and we'll just have a few blocks of extra
+ // misses on soft-fork activation.
+ //
+ // This is also useful in case of bugs in the standard flags that cause
// transactions to pass as valid when they're actually invalid. For
// instance the STRICTENC flag was incorrectly allowing certain
// CHECKSIG NOT scripts to pass, even though they were invalid.
//
// There is a similar check in CreateNewBlock() to prevent creating
- // invalid blocks, however allowing such transactions into the mempool
- // can be exploited as a DoS attack.
- if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata))
+ // invalid blocks (using TestBlockValidity), however allowing such
+ // transactions into the mempool can be exploited as a DoS attack.
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
+ if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata))
{
- return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
- __func__, hash.ToString(), FormatStateMessage(state));
+ // If we're using promiscuousmempoolflags, we may hit this normally
+ // Check if current block has some flags that scriptVerifyFlags
+ // does not before printing an ominous warning
+ if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) {
+ return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against latest-block but not STANDARD flags %s, %s",
+ __func__, hash.ToString(), FormatStateMessage(state));
+ } else {
+ if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, false, txdata)) {
+ return error("%s: ConnectInputs failed against MANDATORY but not STANDARD flags due to promiscuous mempool %s, %s",
+ __func__, hash.ToString(), FormatStateMessage(state));
+ } else {
+ LogPrintf("Warning: -promiscuousmempool flags set to not include currently enforced soft forks, this may break mining or otherwise cause instability!\n");
+ }
+ }
}
// Remove conflicting transactions from the mempool
@@ -1152,12 +1212,34 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
return pindexPrev->nHeight + 1;
}
+
+static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache;
+static uint256 scriptExecutionCacheNonce(GetRandHash());
+
+void InitScriptExecutionCache() {
+ // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
+ // setup_bytes creates the minimum possible cache (2 elements).
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize);
+ LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
+ (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
+}
+
/**
* Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
- * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
- * instead of being performed inline.
+ * This does not modify the UTXO set.
+ *
+ * If pvChecks is not NULL, script checks are pushed onto it instead of being performed inline. Any
+ * script checks which are not necessary (eg due to script execution cache hits) are, obviously,
+ * not pushed onto pvChecks/run.
+ *
+ * Setting cacheSigStore/cacheFullScriptStore to false will remove elements from the corresponding cache
+ * which are matched. This is useful for checking blocks where we will likely never need the cache
+ * entry again.
+ *
+ * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
*/
-static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
{
if (!tx.IsCoinBase())
{
@@ -1177,6 +1259,21 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C
// Of course, if an assumed valid block is invalid due to false scriptSigs
// this optimization would allow an invalid chain to be accepted.
if (fScriptChecks) {
+ // First check if script executions have been cached with the same
+ // flags. Note that this assumes that the inputs provided are
+ // correct (ie that the transaction hash which is in tx's prevouts
+ // properly commits to the scriptPubKey in the inputs view of that
+ // transaction).
+ uint256 hashCacheEntry;
+ // We only use the first 19 bytes of nonce to avoid a second SHA
+ // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
+ static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
+ CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
+ AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
+ if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ return true;
+ }
+
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
@@ -1191,7 +1288,7 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C
const CAmount amount = coin.out.nValue;
// Verify signature
- CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore, &txdata);
+ CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@@ -1204,7 +1301,7 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C
// avoid splitting the network between upgraded and
// non-upgraded nodes.
CScriptCheck check2(scriptPubKey, amount, tx, i,
- flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
+ flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
@@ -1218,6 +1315,12 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
}
}
+
+ if (cacheFullScriptStore && !pvChecks) {
+ // We executed all of the provided scripts, and were told to
+ // cache the result. Do so now.
+ scriptExecutionCache.insert(hashCacheEntry);
+ }
}
}
@@ -1331,17 +1434,19 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
return DISCONNECT_FAILED; // adding output for transaction without known metadata
}
}
- view.AddCoin(out, std::move(undo), undo.fCoinBase);
+ // The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
+ // sure that the coin did not already exist in the cache. As we have queried for that above
+ // using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
+ // it is an overwrite.
+ view.AddCoin(out, std::move(undo), !fClean);
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
- * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */
+ * When FAILED is returned, view is left in an indeterminate state. */
static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
{
- assert(pindex->GetBlockHash() == view.GetBestBlock());
-
bool fClean = true;
CBlockUndo blockUndo;
@@ -1364,6 +1469,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
for (int i = block.vtx.size() - 1; i >= 0; i--) {
const CTransaction &tx = *(block.vtx[i]);
uint256 hash = tx.GetHash();
+ bool is_coinbase = tx.IsCoinBase();
// Check that all outputs are available and match the outputs in the block itself
// exactly.
@@ -1372,7 +1478,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
COutPoint out(hash, o);
Coin coin;
bool is_spent = view.SpendCoin(out, &coin);
- if (!is_spent || tx.vout[o] != coin.out) {
+ if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
fClean = false; // transaction output mismatch
}
}
@@ -1462,12 +1568,12 @@ private:
public:
WarningBitsConditionChecker(int bitIn) : bit(bitIn) {}
- int64_t BeginTime(const Consensus::Params& params) const { return 0; }
- int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits<int64_t>::max(); }
- int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; }
- int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; }
+ int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
+ int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
+ int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
+ int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
- bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const
+ bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
{
return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
((pindex->nVersion >> bit) & 1) != 0 &&
@@ -1478,6 +1584,41 @@ public:
// Protected by cs_main
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
+ AssertLockHeld(cs_main);
+
+ // BIP16 didn't become active until Apr 1 2012
+ int64_t nBIP16SwitchTime = 1333238400;
+ bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
+
+ unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
+
+ // Start enforcing the DERSIG (BIP66) rule
+ if (pindex->nHeight >= consensusparams.BIP66Height) {
+ flags |= SCRIPT_VERIFY_DERSIG;
+ }
+
+ // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule
+ if (pindex->nHeight >= consensusparams.BIP65Height) {
+ flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ }
+
+ // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
+ if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) {
+ flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ }
+
+ // Start enforcing WITNESS rules using versionbits logic.
+ if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
+ flags |= SCRIPT_VERIFY_WITNESS;
+ flags |= SCRIPT_VERIFY_NULLDUMMY;
+ }
+
+ return flags;
+}
+
+
+
static int64_t nTimeCheck = 0;
static int64_t nTimeForks = 0;
static int64_t nTimeVerify = 0;
@@ -1581,34 +1722,14 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
}
}
- // BIP16 didn't become active until Apr 1 2012
- int64_t nBIP16SwitchTime = 1333238400;
- bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
-
- unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
-
- // Start enforcing the DERSIG (BIP66) rule
- if (pindex->nHeight >= chainparams.GetConsensus().BIP66Height) {
- flags |= SCRIPT_VERIFY_DERSIG;
- }
-
- // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule
- if (pindex->nHeight >= chainparams.GetConsensus().BIP65Height) {
- flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
- }
-
// Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
int nLockTimeFlags = 0;
if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) {
- flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
- // Start enforcing WITNESS rules using versionbits logic.
- if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus())) {
- flags |= SCRIPT_VERIFY_WITNESS;
- flags |= SCRIPT_VERIFY_NULLDUMMY;
- }
+ // Get the script flags for this block
+ unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus());
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001);
@@ -1669,7 +1790,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
- if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL))
+ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL))
return error("ConnectBlock(): CheckInputs on %s failed with %s",
tx.GetHash().ToString(), FormatStateMessage(state));
control.Add(vChecks);
@@ -1779,7 +1900,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
nLastSetChain = nNow;
}
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
+ int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
@@ -1946,6 +2067,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip);
+ assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
bool flushed = view.Flush();
@@ -2219,7 +2341,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
nHeight = nTargetHeight;
// Connect new blocks.
- BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
+ for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
@@ -3417,20 +3539,25 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
+ return true;
+}
+
+void LoadChainTip(const CChainParams& chainparams)
+{
+ if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return;
+
// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
- return true;
+ return;
chainActive.SetTip(it->second);
PruneBlockIndexCandidates();
- LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__,
+ LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
-
- return true;
}
CVerifyDB::CVerifyDB()
@@ -3499,6 +3626,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
@@ -3538,6 +3666,92 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return true;
}
+/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
+static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
+{
+ // TODO: merge with ConnectBlock
+ CBlock block;
+ if (!ReadBlockFromDisk(block, pindex, params.GetConsensus())) {
+ return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ }
+
+ for (const CTransactionRef& tx : block.vtx) {
+ if (!tx->IsCoinBase()) {
+ for (const CTxIn &txin : tx->vin) {
+ inputs.SpendCoin(txin.prevout);
+ }
+ }
+ // Pass check = true as every addition may be an overwrite.
+ AddCoins(inputs, *tx, pindex->nHeight, true);
+ }
+ return true;
+}
+
+bool ReplayBlocks(const CChainParams& params, CCoinsView* view)
+{
+ LOCK(cs_main);
+
+ CCoinsViewCache cache(view);
+
+ std::vector<uint256> hashHeads = view->GetHeadBlocks();
+ if (hashHeads.empty()) return true; // We're already in a consistent state.
+ if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state");
+
+ uiInterface.ShowProgress(_("Replaying blocks..."), 0);
+ LogPrintf("Replaying blocks\n");
+
+ const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush.
+ const CBlockIndex* pindexNew; // New tip during the interrupted flush.
+ const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip.
+
+ if (mapBlockIndex.count(hashHeads[0]) == 0) {
+ return error("ReplayBlocks(): reorganization to unknown block requested");
+ }
+ pindexNew = mapBlockIndex[hashHeads[0]];
+
+ if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
+ if (mapBlockIndex.count(hashHeads[1]) == 0) {
+ return error("ReplayBlocks(): reorganization from unknown block requested");
+ }
+ pindexOld = mapBlockIndex[hashHeads[1]];
+ pindexFork = LastCommonAncestor(pindexOld, pindexNew);
+ assert(pindexFork != nullptr);
+ }
+
+ // Rollback along the old branch.
+ while (pindexOld != pindexFork) {
+ if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
+ CBlock block;
+ if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) {
+ return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
+ }
+ LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);
+ DisconnectResult res = DisconnectBlock(block, pindexOld, cache);
+ if (res == DISCONNECT_FAILED) {
+ return error("RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
+ }
+ // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was
+ // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations
+ // applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations,
+ // the result is still a version of the UTXO set with the effects of that block undone.
+ }
+ pindexOld = pindexOld->pprev;
+ }
+
+ // Roll forward from the forking point to the new tip.
+ int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
+ for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
+ const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
+ LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
+ if (!RollforwardBlock(pindex, cache, params)) return false;
+ }
+
+ cache.SetBestBlock(pindexNew->GetBlockHash());
+ cache.Flush();
+ uiInterface.ShowProgress("", 100);
+ return true;
+}
+
bool RewindBlockIndex(const CChainParams& params)
{
LOCK(cs_main);
@@ -3687,8 +3901,6 @@ bool InitBlockIndex(const CChainParams& chainparams)
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("LoadBlockIndex(): genesis block not accepted");
- // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
- return FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
} catch (const std::runtime_error& e) {
return error("LoadBlockIndex(): failed to initialize block database: %s", e.what());
}
diff --git a/src/validation.h b/src/validation.h
index 82df4cb170..a9f995abb8 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -260,6 +260,8 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
bool InitBlockIndex(const CChainParams& chainparams);
/** Load the block tree and coins database from disk */
bool LoadBlockIndex(const CChainParams& chainparams);
+/** Update the chain tip based on database information. */
+void LoadChainTip(const CChainParams& chainparams);
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
@@ -391,6 +393,9 @@ public:
ScriptError GetScriptError() const { return error; }
};
+/** Initializes the script-execution cache */
+void InitScriptExecutionCache();
+
/** Functions for disk access for blocks */
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
@@ -424,6 +429,9 @@ public:
bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
};
+/** Replay blocks that aren't fully applied to the database. */
+bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
+
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 46d7c9b329..bf20d606f8 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -4,49 +4,123 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "validationinterface.h"
+#include "init.h"
+#include "scheduler.h"
+#include "sync.h"
+#include "util.h"
+
+#include <list>
+#include <atomic>
+
+#include <boost/signals2/signal.hpp>
+
+struct MainSignalsInstance {
+ boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
+ boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
+ boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
+ boost::signals2::signal<void (const uint256 &)> Inventory;
+ boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
+ boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
+ boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
+
+ // We are not allowed to assume the scheduler only runs in one thread,
+ // but must ensure all callbacks happen in-order, so we end up creating
+ // our own queue here :(
+ SingleThreadedSchedulerClient m_schedulerClient;
+
+ MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
+};
static CMainSignals g_signals;
+void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
+ assert(!m_internals);
+ m_internals.reset(new MainSignalsInstance(&scheduler));
+}
+
+void CMainSignals::UnregisterBackgroundSignalScheduler() {
+ m_internals.reset(nullptr);
+}
+
+void CMainSignals::FlushBackgroundCallbacks() {
+ m_internals->m_schedulerClient.EmptyQueue();
+}
+
CMainSignals& GetMainSignals()
{
return g_signals;
}
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
- g_signals.BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
- g_signals.BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
- g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
- g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
- g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
- g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
- g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
+ 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->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
+ g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, 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));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
- g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
- g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
- g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
- g_signals.TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
- g_signals.BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
- g_signals.BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
- g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
+ 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->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
+ g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, 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->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
+ g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
- g_signals.ScriptForMining.disconnect_all_slots();
- g_signals.BlockChecked.disconnect_all_slots();
- g_signals.Broadcast.disconnect_all_slots();
- g_signals.Inventory.disconnect_all_slots();
- g_signals.SetBestChain.disconnect_all_slots();
- g_signals.TransactionAddedToMempool.disconnect_all_slots();
- g_signals.BlockConnected.disconnect_all_slots();
- g_signals.BlockDisconnected.disconnect_all_slots();
- g_signals.UpdatedBlockTip.disconnect_all_slots();
- g_signals.NewPoWValidBlock.disconnect_all_slots();
+ g_signals.m_internals->BlockChecked.disconnect_all_slots();
+ g_signals.m_internals->Broadcast.disconnect_all_slots();
+ g_signals.m_internals->Inventory.disconnect_all_slots();
+ g_signals.m_internals->SetBestChain.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->UpdatedBlockTip.disconnect_all_slots();
+ g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots();
+}
+
+void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+ m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
+}
+
+void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
+ m_internals->TransactionAddedToMempool(ptx);
+}
+
+void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+ m_internals->BlockConnected(pblock, pindex, vtxConflicted);
+}
+
+void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
+ m_internals->BlockDisconnected(pblock);
+}
+
+void CMainSignals::SetBestChain(const CBlockLocator &locator) {
+ m_internals->SetBestChain(locator);
+}
+
+void CMainSignals::Inventory(const uint256 &hash) {
+ m_internals->Inventory(hash);
+}
+
+void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
+ m_internals->Broadcast(nBestBlockTime, connman);
+}
+
+void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) {
+ m_internals->BlockChecked(block, state);
+}
+
+void CMainSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
+ m_internals->NewPoWValidBlock(pindex, block);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 460aecf243..568da66df2 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_VALIDATIONINTERFACE_H
#define BITCOIN_VALIDATIONINTERFACE_H
-#include <boost/signals2/signal.hpp>
#include <memory>
#include "primitives/transaction.h" // CTransaction(Ref)
@@ -20,6 +19,7 @@ class CReserveScript;
class CValidationInterface;
class CValidationState;
class uint256;
+class CScheduler;
// These functions dispatch to one or all registered wallets
@@ -32,52 +32,66 @@ void UnregisterAllValidationInterfaces();
class CValidationInterface {
protected:
- virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
- virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
- virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
- virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
- virtual void SetBestChain(const CBlockLocator &locator) {}
- virtual void Inventory(const uint256 &hash) {}
- virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
- virtual void BlockChecked(const CBlock&, const CValidationState&) {}
- virtual void GetScriptForMining(std::shared_ptr<CReserveScript>&) {};
- virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
- friend void ::RegisterValidationInterface(CValidationInterface*);
- friend void ::UnregisterValidationInterface(CValidationInterface*);
- friend void ::UnregisterAllValidationInterfaces();
-};
-
-struct CMainSignals {
/** Notifies listeners of updated block chain tip */
- boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
+ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
/** Notifies listeners of a transaction having been added to mempool. */
- boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
+ virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
/**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
*/
- boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &)> BlockConnected;
+ virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
/** Notifies listeners of a block being disconnected */
- boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
- /** Notifies listeners of a new active block chain. */
- boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
+ virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
+ /** Notifies listeners of the new active block chain on-disk. */
+ virtual void SetBestChain(const CBlockLocator &locator) {}
/** Notifies listeners about an inventory item being seen on the network. */
- boost::signals2::signal<void (const uint256 &)> Inventory;
+ virtual void Inventory(const uint256 &hash) {}
/** Tells listeners to broadcast their data. */
- boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
+ virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
* Notifies listeners of a block validation result.
* If the provided CValidationState IsValid, the provided block
* is guaranteed to be the current best block at the time the
* callback was generated (not necessarily now)
*/
- boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
- /** Notifies listeners that a key for mining is required (coinbase) */
- boost::signals2::signal<void (std::shared_ptr<CReserveScript>&)> ScriptForMining;
+ virtual void BlockChecked(const CBlock&, const CValidationState&) {}
/**
* Notifies listeners that a block which builds directly on our current tip
* has been received and connected to the headers tree, though not validated yet */
- boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
+ virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
+ friend void ::RegisterValidationInterface(CValidationInterface*);
+ friend void ::UnregisterValidationInterface(CValidationInterface*);
+ friend void ::UnregisterAllValidationInterfaces();
+};
+
+struct MainSignalsInstance;
+class CMainSignals {
+private:
+ std::unique_ptr<MainSignalsInstance> m_internals;
+
+ friend void ::RegisterValidationInterface(CValidationInterface*);
+ friend void ::UnregisterValidationInterface(CValidationInterface*);
+ friend void ::UnregisterAllValidationInterfaces();
+
+public:
+ /** Register a CScheduler to give callbacks which should run in the background (may only be called once) */
+ void RegisterBackgroundSignalScheduler(CScheduler& scheduler);
+ /** Unregister a CScheduler to give callbacks which should run in the background - these callbacks will now be dropped! */
+ void UnregisterBackgroundSignalScheduler();
+ /** Call any remaining callbacks on the calling thread */
+ void FlushBackgroundCallbacks();
+
+ void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
+ void TransactionAddedToMempool(const CTransactionRef &);
+ void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &);
+ void BlockDisconnected(const std::shared_ptr<const CBlock> &);
+ void UpdatedTransaction(const uint256 &);
+ void SetBestChain(const CBlockLocator &);
+ void Inventory(const uint256 &);
+ void Broadcast(int64_t nBestBlockTime, CConnman* connman);
+ void BlockChecked(const CBlock&, const CValidationState&);
+ void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
};
CMainSignals& GetMainSignals();
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 92c90b7efb..8047e17aa8 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -174,12 +174,12 @@ private:
const Consensus::DeploymentPos id;
protected:
- int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; }
- int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; }
- int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; }
- int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; }
+ int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
+ int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
+ int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
+ int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
- bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const
+ bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
{
return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0);
}
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index cb4719ae90..bdd01bec12 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -6,6 +6,7 @@
#define BITCOIN_WALLET_COINCONTROL_H
#include "policy/feerate.h"
+#include "policy/fees.h"
#include "primitives/transaction.h"
#include "wallet/wallet.h"
@@ -26,6 +27,8 @@ public:
int nConfirmTarget;
//! Signal BIP-125 replace by fee.
bool signalRbf;
+ //! Fee estimation mode to control arguments to estimateSmartFee
+ FeeEstimateMode m_fee_mode;
CCoinControl()
{
@@ -42,6 +45,7 @@ public:
fOverrideFeeRate = false;
nConfirmTarget = 0;
signalRbf = fWalletRbf;
+ m_fee_mode = FeeEstimateMode::UNSET;
}
bool HasSelected() const
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 6fa685628f..dcce88cedc 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -12,7 +12,6 @@
#include <string>
#include <vector>
-#include <boost/foreach.hpp>
int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const
{
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index f1c4f57428..1dc44e424f 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -157,8 +157,8 @@ public:
bool Lock();
virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
- bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
- bool HaveKey(const CKeyID &address) const
+ bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
+ bool HaveKey(const CKeyID &address) const override
{
{
LOCK(cs_KeyStore);
@@ -168,9 +168,9 @@ public:
}
return false;
}
- bool GetKey(const CKeyID &address, CKey& keyOut) const;
- bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
- void GetKeys(std::set<CKeyID> &setAddress) const
+ bool GetKey(const CKeyID &address, CKey& keyOut) const override;
+ bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ void GetKeys(std::set<CKeyID> &setAddress) const override
{
if (!IsCrypted())
{
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index c8928a3b66..da2d180756 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -18,7 +18,6 @@
#include <sys/stat.h>
#endif
-#include <boost/foreach.hpp>
#include <boost/thread.hpp>
//
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 6a9e6cf9ff..607ecf4182 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -66,7 +66,7 @@ bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx
return true;
}
-CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable)
+CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode)
:
txid(std::move(txidIn)),
nOldFee(0),
@@ -165,7 +165,8 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
nNewFee = totalFee;
nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
} else {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr, ignoreGlobalPayTxFee);
+ bool conservative_estimate = CalculateEstimateType(fee_mode, newTxReplaceable);
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr /* FeeCalculation */, ignoreGlobalPayTxFee, conservative_estimate);
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
// New fee rate must be at least old rate + minimum incremental relay rate
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index fc32316704..11e2f5f953 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -10,6 +10,7 @@
class CWallet;
class CWalletTx;
class uint256;
+enum class FeeEstimateMode;
enum class BumpFeeResult
{
@@ -24,7 +25,7 @@ enum class BumpFeeResult
class CFeeBumper
{
public:
- CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable);
+ CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode);
BumpFeeResult getResult() const { return currentResult; }
const std::vector<std::string>& getErrors() const { return vErrors; }
CAmount getOldFee() const { return nOldFee; }
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 0fe2412352..9f42b1f266 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -26,7 +26,6 @@
#include <univalue.h>
-#include <boost/foreach.hpp>
std::string static EncodeDumpTime(int64_t nTime) {
return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
@@ -362,7 +361,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
"\nExamples:\n"
+ HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
"\nAs a JSON-RPC call\n"
- + HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
+ + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
);
LOCK2(cs_main, pwallet->cs_wallet);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 5bbb5088e2..5f72e3b6f5 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -15,6 +15,7 @@
#include "policy/fees.h"
#include "policy/policy.h"
#include "policy/rbf.h"
+#include "rpc/mining.h"
#include "rpc/server.h"
#include "script/sign.h"
#include "timedata.h"
@@ -355,7 +356,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
return ret;
}
-static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew)
+static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, CCoinControl *coin_control = nullptr)
{
CAmount curBalance = pwallet->GetBalance();
@@ -381,7 +382,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
- if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
+ if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
@@ -400,9 +401,9 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n"
+ "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n"
"\nSend an amount to a given address.\n"
+ HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
@@ -415,6 +416,12 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
" 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"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
"\nResult:\n"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n"
@@ -443,12 +450,29 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
wtx.mapValue["to"] = request.params[3].get_str();
bool fSubtractFeeFromAmount = false;
- if (request.params.size() > 4)
+ if (request.params.size() > 4 && !request.params[4].isNull()) {
fSubtractFeeFromAmount = request.params[4].get_bool();
+ }
+
+ CCoinControl coin_control;
+ if (request.params.size() > 5 && !request.params[5].isNull()) {
+ coin_control.signalRbf = request.params[5].get_bool();
+ }
+
+ if (request.params.size() > 6 && !request.params[6].isNull()) {
+ coin_control.nConfirmTarget = request.params[6].get_int();
+ }
+
+ if (request.params.size() > 7 && !request.params[7].isNull()) {
+ if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ }
+
EnsureWalletIsUnlocked(pwallet);
- SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx);
+ SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, &coin_control);
return wtx.GetHash().GetHex();
}
@@ -887,9 +911,9 @@ UniValue sendmany(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n"
+ "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
@@ -909,7 +933,13 @@ UniValue sendmany(const JSONRPCRequest& request)
" \"address\" (string) Subtract fee from this address\n"
" ,...\n"
" ]\n"
- "\nResult:\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"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ "\nResult:\n"
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
@@ -941,9 +971,24 @@ UniValue sendmany(const JSONRPCRequest& request)
wtx.mapValue["comment"] = request.params[3].get_str();
UniValue subtractFeeFromAmount(UniValue::VARR);
- if (request.params.size() > 4)
+ if (request.params.size() > 4 && !request.params[4].isNull())
subtractFeeFromAmount = request.params[4].get_array();
+ CCoinControl coin_control;
+ if (request.params.size() > 5 && !request.params[5].isNull()) {
+ coin_control.signalRbf = request.params[5].get_bool();
+ }
+
+ if (request.params.size() > 6 && !request.params[6].isNull()) {
+ coin_control.nConfirmTarget = request.params[6].get_int();
+ }
+
+ if (request.params.size() > 7 && !request.params[7].isNull()) {
+ if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ }
+
std::set<CBitcoinAddress> setAddress;
std::vector<CRecipient> vecSend;
@@ -988,7 +1033,7 @@ UniValue sendmany(const JSONRPCRequest& request)
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
std::string strFailReason;
- bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
+ bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, &coin_control);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
@@ -2655,7 +2700,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" 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"
- " \"optIntoRbf\" (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees\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"
"\nResult:\n"
@@ -2707,7 +2758,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"reserveChangeKey", UniValueType(UniValue::VBOOL)},
{"feeRate", UniValueType()}, // will be checked below
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
- {"optIntoRbf", UniValueType(UniValue::VBOOL)},
+ {"replaceable", UniValueType(UniValue::VBOOL)},
+ {"conf_target", UniValueType(UniValue::VNUM)},
+ {"estimate_mode", UniValueType(UniValue::VSTR)},
},
true, true);
@@ -2741,8 +2794,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("subtractFeeFromOutputs"))
subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
- if (options.exists("optIntoRbf")) {
- coinControl.signalRbf = options["optIntoRbf"].get_bool();
+ if (options.exists("replaceable")) {
+ coinControl.signalRbf = options["replaceable"].get_bool();
+ }
+ if (options.exists("conf_target")) {
+ coinControl.nConfirmTarget = options["conf_target"].get_int();
+ }
+ if (options.exists("estimate_mode")) {
+ if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
}
}
}
@@ -2821,6 +2882,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
" 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"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
" }\n"
"\nResult:\n"
"{\n"
@@ -2843,6 +2908,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
int newConfirmTarget = nTxConfirmTarget;
CAmount totalFee = 0;
bool replaceable = true;
+ FeeEstimateMode fee_mode = FeeEstimateMode::UNSET;
if (request.params.size() > 1) {
UniValue options = request.params[1];
RPCTypeCheckObj(options,
@@ -2850,6 +2916,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
{"confTarget", UniValueType(UniValue::VNUM)},
{"totalFee", UniValueType(UniValue::VNUM)},
{"replaceable", UniValueType(UniValue::VBOOL)},
+ {"estimate_mode", UniValueType(UniValue::VSTR)},
},
true, true);
@@ -2874,12 +2941,17 @@ UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("replaceable")) {
replaceable = options["replaceable"].get_bool();
}
+ if (options.exists("estimate_mode")) {
+ if (!FeeModeFromString(options["estimate_mode"].get_str(), fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ }
}
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable);
+ CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable, fee_mode);
BumpFeeResult res = feeBump.getResult();
if (res != BumpFeeResult::OK)
{
@@ -2922,6 +2994,51 @@ UniValue bumpfee(const JSONRPCRequest& request)
return result;
}
+UniValue generate(const JSONRPCRequest& request)
+{
+ CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw std::runtime_error(
+ "generate nblocks ( maxtries )\n"
+ "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
+ "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
+ "\nResult:\n"
+ "[ blockhashes ] (array) hashes of blocks generated\n"
+ "\nExamples:\n"
+ "\nGenerate 11 blocks\n"
+ + HelpExampleCli("generate", "11")
+ );
+ }
+
+ int num_generate = request.params[0].get_int();
+ uint64_t max_tries = 1000000;
+ if (request.params.size() > 1 && !request.params[1].isNull()) {
+ max_tries = request.params[1].get_int();
+ }
+
+ std::shared_ptr<CReserveScript> coinbase_script;
+ pwallet->GetScriptForMining(coinbase_script);
+
+ // If the keypool is exhausted, no script is returned at all. Catch this.
+ if (!coinbase_script) {
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
+ }
+
+ //throw an error if no script was provided
+ if (coinbase_script->reserveScript.empty()) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
+ }
+
+ return generateBlocks(coinbase_script, num_generate, max_tries, true);
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -2976,8 +3093,8 @@ static const CRPCCommand commands[] =
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
- { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
- { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} },
+ { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
+ { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
{ "wallet", "setaccount", &setaccount, true, {"address","account"} },
{ "wallet", "settxfee", &settxfee, true, {"amount"} },
{ "wallet", "signmessage", &signmessage, true, {"address","message"} },
@@ -2985,6 +3102,8 @@ static const CRPCCommand commands[] =
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} },
+
+ { "generating", "generate", &generate, true, {"nblocks","maxtries"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index 12d9f2e995..330878ceb5 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -8,7 +8,6 @@
#include <stdint.h>
-#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
extern CWallet* pwalletMain;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 0d1a86dd24..a1b4eb106e 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1718,7 +1718,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
- throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
+ throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}
@@ -1761,7 +1761,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
- throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
}
}
@@ -2724,7 +2724,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget;
- CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc);
+ // Allow to override the default fee estimate mode over the CoinControl instance
+ bool conservative_estimate = CalculateEstimateType(coinControl ? coinControl->m_fee_mode : FeeEstimateMode::UNSET, rbf);
+
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc, false /* ignoreGlobalPayTxFee */, conservative_estimate);
if (coinControl && coinControl->fOverrideFeeRate)
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
@@ -2905,13 +2908,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
}
-CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee)
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate)
{
// payTxFee is the user-set global for desired feerate
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0 || ignoreGlobalPayTxFee) {
- nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, true).GetFee(nTxBytes);
+ nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, conservative_estimate).GetFee(nTxBytes);
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
if (nFeeNeeded == 0) {
nFeeNeeded = fallbackFee.GetFee(nTxBytes);
@@ -4154,3 +4157,15 @@ bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
{
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}
+
+bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf) {
+ switch (mode) {
+ case FeeEstimateMode::UNSET:
+ return !opt_in_rbf; // Allow for lower fees if RBF is an option
+ case FeeEstimateMode::CONSERVATIVE:
+ return true;
+ case FeeEstimateMode::ECONOMICAL:
+ return false;
+ }
+ return true;
+}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 6ed955cf58..e3715cdf37 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -80,6 +80,7 @@ class CTxMemPool;
class CBlockPolicyEstimator;
class CWalletTx;
struct FeeCalculation;
+enum class FeeEstimateMode;
/** (client) version numbers for particular wallet features */
enum WalletFeature
@@ -963,7 +964,7 @@ public:
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
- static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc = nullptr, bool ignoreGlobalPayTxFee = false);
+ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate);
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
@@ -1025,7 +1026,7 @@ public:
}
}
- void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override;
+ void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize()
{
@@ -1153,7 +1154,7 @@ public:
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey, bool internal = false);
void KeepKey();
- void KeepScript() { KeepKey(); }
+ void KeepScript() override { KeepKey(); }
};
@@ -1211,4 +1212,7 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins
}
return true;
}
+
+bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf);
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 8321719b56..deb09a4771 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -18,7 +18,6 @@
#include <atomic>
-#include <boost/foreach.hpp>
#include <boost/thread.hpp>
//
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index bcbecf1bde..1790fe5698 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -24,32 +24,32 @@ public:
*/
bool SendMessage(const char *command, const void* data, size_t size);
- bool Initialize(void *pcontext);
- void Shutdown();
+ bool Initialize(void *pcontext) override;
+ void Shutdown() override;
};
class CZMQPublishHashBlockNotifier : public CZMQAbstractPublishNotifier
{
public:
- bool NotifyBlock(const CBlockIndex *pindex);
+ bool NotifyBlock(const CBlockIndex *pindex) override;
};
class CZMQPublishHashTransactionNotifier : public CZMQAbstractPublishNotifier
{
public:
- bool NotifyTransaction(const CTransaction &transaction);
+ bool NotifyTransaction(const CTransaction &transaction) override;
};
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier
{
public:
- bool NotifyBlock(const CBlockIndex *pindex);
+ bool NotifyBlock(const CBlockIndex *pindex) override;
};
class CZMQPublishRawTransactionNotifier : public CZMQAbstractPublishNotifier
{
public:
- bool NotifyTransaction(const CTransaction &transaction);
+ bool NotifyTransaction(const CTransaction &transaction) override;
};
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py
index b90b0ca628..f00232c9ff 100755
--- a/test/functional/bip9-softforks.py
+++ b/test/functional/bip9-softforks.py
@@ -15,6 +15,10 @@ mine a further 143 blocks (LOCKED_IN)
test that enforcement has not triggered (which triggers ACTIVE)
test that enforcement has triggered
"""
+from io import BytesIO
+import shutil
+import time
+import itertools
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
@@ -22,9 +26,6 @@ from test_framework.mininode import CTransaction, NetworkThread
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
-from io import BytesIO
-import time
-import itertools
class BIP9SoftForksTest(ComparisonTestFramework):
diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py
index e205c6400c..a7034e6bcd 100755
--- a/test/functional/blockchain.py
+++ b/test/functional/blockchain.py
@@ -18,16 +18,16 @@ Tests correspond to code in rpc/blockchain.cpp.
"""
from decimal import Decimal
+import http.client
import subprocess
-from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_framework import (BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT)
from test_framework.util import (
assert_equal,
assert_raises,
assert_raises_jsonrpc,
assert_is_hex_string,
assert_is_hash_string,
- bitcoind_processes,
)
@@ -139,10 +139,13 @@ class BlockchainTest(BitcoinTestFramework):
self.nodes[0].generate(6)
assert_equal(self.nodes[0].getblockcount(), 206)
self.log.debug('Node should not stop at this height')
- assert_raises(subprocess.TimeoutExpired, lambda: bitcoind_processes[0].wait(timeout=3))
- self.nodes[0].generate(1)
+ assert_raises(subprocess.TimeoutExpired, lambda: self.bitcoind_processes[0].wait(timeout=3))
+ try:
+ self.nodes[0].generate(1)
+ except (ConnectionError, http.client.BadStatusLine):
+ pass # The node already shut down before response
self.log.debug('Node should stop at this height...')
- bitcoind_processes[0].wait(timeout=3)
+ self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
self.nodes[0] = self.start_node(0, self.options.tmpdir)
assert_equal(self.nodes[0].getblockcount(), 207)
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index 569db7ced5..9237f09240 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -42,7 +42,7 @@ class BumpFeeTest(BitcoinTestFramework):
# Encrypt wallet for test_locked_wallet_fails test
self.nodes[1].encryptwallet(WALLET_PASSPHRASE)
- bitcoind_processes[1].wait()
+ self.bitcoind_processes[1].wait()
self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1])
self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py
new file mode 100755
index 0000000000..8339305f5e
--- /dev/null
+++ b/test/functional/dbcrash.py
@@ -0,0 +1,281 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test recovery from a crash during chainstate writing.
+
+- 4 nodes
+ * node0, node1, and node2 will have different dbcrash ratios, and different
+ dbcache sizes
+ * node3 will be a regular node, with no crashing.
+ * The nodes will not connect to each other.
+
+- use default test framework starting chain. initialize starting_tip_height to
+ tip height.
+
+- Main loop:
+ * generate lots of transactions on node3, enough to fill up a block.
+ * uniformly randomly pick a tip height from starting_tip_height to
+ tip_height; with probability 1/(height_difference+4), invalidate this block.
+ * mine enough blocks to overtake tip_height at start of loop.
+ * for each node in [node0,node1,node2]:
+ - for each mined block:
+ * submit block to node
+ * if node crashed on/after submitting:
+ - restart until recovery succeeds
+ - check that utxo matches node3 using gettxoutsetinfo"""
+
+import errno
+import http.client
+import random
+import sys
+import time
+
+from test_framework.mininode import *
+from test_framework.script import *
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+HTTP_DISCONNECT_ERRORS = [http.client.CannotSendRequest]
+try:
+ HTTP_DISCONNECT_ERRORS.append(http.client.RemoteDisconnected)
+except AttributeError:
+ pass
+
+class ChainstateWriteCrashTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 4
+ self.setup_clean_chain = False
+
+ # Set -maxmempool=0 to turn off mempool memory sharing with dbcache
+ # Set -rpcservertimeout=900 to reduce socket disconnects in this
+ # long-running test
+ self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"]
+
+ # Set different crash ratios and cache sizes. Note that not all of
+ # -dbcache goes to pcoinsTip.
+ self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args
+ self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args
+ self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args
+
+ # Node3 is a normal node with default args, except will mine full blocks
+ self.node3_args = ["-blockmaxweight=4000000"]
+ self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
+
+ def setup_network(self):
+ self.setup_nodes()
+ # Leave them unconnected, we'll use submitblock directly in this test
+
+ def restart_node(self, node_index, expected_tip):
+ """Start up a given node id, wait for the tip to reach the given block hash, and calculate the utxo hash.
+
+ Exceptions on startup should indicate node crash (due to -dbcrashratio), in which case we try again. Give up
+ after 60 seconds. Returns the utxo hash of the given node."""
+
+ time_start = time.time()
+ while time.time() - time_start < 60:
+ try:
+ # Any of these RPC calls could throw due to node crash
+ self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index])
+ self.nodes[node_index].waitforblock(expected_tip)
+ utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2']
+ return utxo_hash
+ except:
+ # An exception here should mean the node is about to crash.
+ # If bitcoind exits, then try again. wait_for_node_exit()
+ # should raise an exception if bitcoind doesn't exit.
+ self.wait_for_node_exit(node_index, timeout=10)
+ self.crashed_on_restart += 1
+ time.sleep(1)
+
+ # If we got here, bitcoind isn't coming back up on restart. Could be a
+ # bug in bitcoind, or we've gotten unlucky with our dbcrash ratio --
+ # perhaps we generated a test case that blew up our cache?
+ # TODO: If this happens a lot, we should try to restart without -dbcrashratio
+ # and make sure that recovery happens.
+ raise AssertionError("Unable to successfully restart node %d in allotted time", node_index)
+
+ def submit_block_catch_error(self, node_index, block):
+ """Try submitting a block to the given node.
+
+ Catch any exceptions that indicate the node has crashed.
+ Returns true if the block was submitted successfully; false otherwise."""
+
+ try:
+ self.nodes[node_index].submitblock(block)
+ return True
+ except http.client.BadStatusLine as e:
+ # Prior to 3.5 BadStatusLine('') was raised for a remote disconnect error.
+ if sys.version_info[0] == 3 and sys.version_info[1] < 5 and e.line == "''":
+ self.log.debug("node %d submitblock raised exception: %s", node_index, e)
+ return False
+ else:
+ raise
+ except tuple(HTTP_DISCONNECT_ERRORS) as e:
+ self.log.debug("node %d submitblock raised exception: %s", node_index, e)
+ return False
+ except OSError as e:
+ self.log.debug("node %d submitblock raised OSError exception: errno=%s", node_index, e.errno)
+ if e.errno in [errno.EPIPE, errno.ECONNREFUSED, errno.ECONNRESET]:
+ # The node has likely crashed
+ return False
+ else:
+ # Unexpected exception, raise
+ raise
+
+ def sync_node3blocks(self, block_hashes):
+ """Use submitblock to sync node3's chain with the other nodes
+
+ If submitblock fails, restart the node and get the new utxo hash.
+ If any nodes crash while updating, we'll compare utxo hashes to
+ ensure recovery was successful."""
+
+ node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_2']
+
+ # Retrieve all the blocks from node3
+ blocks = []
+ for block_hash in block_hashes:
+ blocks.append([block_hash, self.nodes[3].getblock(block_hash, 0)])
+
+ # Deliver each block to each other node
+ for i in range(3):
+ nodei_utxo_hash = None
+ self.log.debug("Syncing blocks to node %d", i)
+ for (block_hash, block) in blocks:
+ # Get the block from node3, and submit to node_i
+ self.log.debug("submitting block %s", block_hash)
+ if not self.submit_block_catch_error(i, block):
+ # TODO: more carefully check that the crash is due to -dbcrashratio
+ # (change the exit code perhaps, and check that here?)
+ self.wait_for_node_exit(i, timeout=30)
+ self.log.debug("Restarting node %d after block hash %s", i, block_hash)
+ nodei_utxo_hash = self.restart_node(i, block_hash)
+ assert nodei_utxo_hash is not None
+ self.restart_counts[i] += 1
+ else:
+ # Clear it out after successful submitblock calls -- the cached
+ # utxo hash will no longer be correct
+ nodei_utxo_hash = None
+
+ # Check that the utxo hash matches node3's utxo set
+ # NOTE: we only check the utxo set if we had to restart the node
+ # after the last block submitted:
+ # - checking the utxo hash causes a cache flush, which we don't
+ # want to do every time; so
+ # - we only update the utxo cache after a node restart, since flushing
+ # the cache is a no-op at that point
+ if nodei_utxo_hash is not None:
+ self.log.debug("Checking txoutsetinfo matches for node %d", i)
+ assert_equal(nodei_utxo_hash, node3_utxo_hash)
+
+ def verify_utxo_hash(self):
+ """Verify that the utxo hash of each node matches node3.
+
+ Restart any nodes that crash while querying."""
+ node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_2']
+ self.log.info("Verifying utxo hash matches for all nodes")
+
+ for i in range(3):
+ try:
+ nodei_utxo_hash = self.nodes[i].gettxoutsetinfo()['hash_serialized_2']
+ except OSError:
+ # probably a crash on db flushing
+ nodei_utxo_hash = self.restart_node(i, self.nodes[3].getbestblockhash())
+ assert_equal(nodei_utxo_hash, node3_utxo_hash)
+
+ def generate_small_transactions(self, node, count, utxo_list):
+ FEE = 1000 # TODO: replace this with node relay fee based calculation
+ num_transactions = 0
+ random.shuffle(utxo_list)
+ while len(utxo_list) >= 2 and num_transactions < count:
+ tx = CTransaction()
+ input_amount = 0
+ for i in range(2):
+ utxo = utxo_list.pop()
+ tx.vin.append(CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout'])))
+ input_amount += int(utxo['amount'] * COIN)
+ output_amount = (input_amount - FEE) // 3
+
+ if output_amount <= 0:
+ # Sanity check -- if we chose inputs that are too small, skip
+ continue
+
+ for i in range(3):
+ tx.vout.append(CTxOut(output_amount, hex_str_to_bytes(utxo['scriptPubKey'])))
+
+ # Sign and send the transaction to get into the mempool
+ tx_signed_hex = node.signrawtransaction(ToHex(tx))['hex']
+ node.sendrawtransaction(tx_signed_hex)
+ num_transactions += 1
+
+ def run_test(self):
+ # Track test coverage statistics
+ self.restart_counts = [0, 0, 0] # Track the restarts for nodes 0-2
+ self.crashed_on_restart = 0 # Track count of crashes during recovery
+
+ # Start by creating a lot of utxos on node3
+ initial_height = self.nodes[3].getblockcount()
+ utxo_list = create_confirmed_utxos(self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000)
+ self.log.info("Prepped %d utxo entries", len(utxo_list))
+
+ # Sync these blocks with the other nodes
+ block_hashes_to_sync = []
+ for height in range(initial_height + 1, self.nodes[3].getblockcount() + 1):
+ block_hashes_to_sync.append(self.nodes[3].getblockhash(height))
+
+ self.log.debug("Syncing %d blocks with other nodes", len(block_hashes_to_sync))
+ # Syncing the blocks could cause nodes to crash, so the test begins here.
+ self.sync_node3blocks(block_hashes_to_sync)
+
+ starting_tip_height = self.nodes[3].getblockcount()
+
+ # Main test loop:
+ # each time through the loop, generate a bunch of transactions,
+ # and then either mine a single new block on the tip, or some-sized reorg.
+ for i in range(40):
+ self.log.info("Iteration %d, generating 2500 transactions %s", i, self.restart_counts)
+ # Generate a bunch of small-ish transactions
+ self.generate_small_transactions(self.nodes[3], 2500, utxo_list)
+ # Pick a random block between current tip, and starting tip
+ current_height = self.nodes[3].getblockcount()
+ random_height = random.randint(starting_tip_height, current_height)
+ self.log.debug("At height %d, considering height %d", current_height, random_height)
+ if random_height > starting_tip_height:
+ # Randomly reorg from this point with some probability (1/4 for
+ # tip, 1/5 for tip-1, ...)
+ if random.random() < 1.0 / (current_height + 4 - random_height):
+ self.log.debug("Invalidating block at height %d", random_height)
+ self.nodes[3].invalidateblock(self.nodes[3].getblockhash(random_height))
+
+ # Now generate new blocks until we pass the old tip height
+ self.log.debug("Mining longer tip")
+ block_hashes = []
+ while current_height + 1 > self.nodes[3].getblockcount():
+ block_hashes.extend(self.nodes[3].generate(min(10, current_height + 1 - self.nodes[3].getblockcount())))
+ self.log.debug("Syncing %d new blocks...", len(block_hashes))
+ self.sync_node3blocks(block_hashes)
+ utxo_list = self.nodes[3].listunspent()
+ self.log.debug("Node3 utxo count: %d", len(utxo_list))
+
+ # Check that the utxo hashes agree with node3
+ # Useful side effect: each utxo cache gets flushed here, so that we
+ # won't get crashes on shutdown at the end of the test.
+ self.verify_utxo_hash()
+
+ # Check the test coverage
+ self.log.info("Restarted nodes: %s; crashes on restart: %d", self.restart_counts, self.crashed_on_restart)
+
+ # If no nodes were restarted, we didn't test anything.
+ assert self.restart_counts != [0, 0, 0]
+
+ # Make sure we tested the case of crash-during-recovery.
+ assert self.crashed_on_restart > 0
+
+ # Warn if any of the nodes escaped restart.
+ for i in range(3):
+ if self.restart_counts[i] == 0:
+ self.log.warn("Node %d never crashed during utxo flush!", i)
+
+if __name__ == "__main__":
+ ChainstateWriteCrashTest().main()
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index 0a3166b89b..0baab6d01c 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the fundrawtransaction RPC."""
-from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT
from test_framework.util import *
@@ -452,7 +452,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.stop_node(2)
self.stop_node(3)
self.nodes[1].encryptwallet("test")
- bitcoind_processes[1].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
+ self.bitcoind_processes[1].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir)
# This test is not meant to test fee estimation and we'd like
diff --git a/test/functional/keypool.py b/test/functional/keypool.py
index f23a427d1f..e8be559918 100755
--- a/test/functional/keypool.py
+++ b/test/functional/keypool.py
@@ -18,7 +18,7 @@ class KeyPoolTest(BitcoinTestFramework):
# Encrypt wallet and wait to terminate
nodes[0].encryptwallet('test')
- bitcoind_processes[0].wait()
+ self.bitcoind_processes[0].wait()
# Restart node 0
nodes[0] = self.start_node(0, self.options.tmpdir)
# Keep creating keys
diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py
index f69f1c5724..f75a8e29cc 100755
--- a/test/functional/listtransactions.py
+++ b/test/functional/listtransactions.py
@@ -23,7 +23,7 @@ class ListTransactionsTest(BitcoinTestFramework):
def setup_nodes(self):
#This test requires mocktime
- enable_mocktime()
+ self.enable_mocktime()
self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir)
def run_test(self):
diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py
index 2cad6269ac..19d99c9c9e 100755
--- a/test/functional/receivedby.py
+++ b/test/functional/receivedby.py
@@ -31,7 +31,7 @@ class ReceivedByTest(BitcoinTestFramework):
def setup_nodes(self):
#This test requires mocktime
- enable_mocktime()
+ self.enable_mocktime()
self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir)
def run_test(self):
diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py
index d6bf3ea59f..bc67654987 100755
--- a/test/functional/replace-by-fee.py
+++ b/test/functional/replace-by-fee.py
@@ -531,8 +531,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(json1["vin"][0]["sequence"], 4294967295)
rawtx2 = self.nodes[0].createrawtransaction([], outs)
- frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": True})
- frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": False})
+ frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
+ frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py
index 198599010e..951685aa76 100755
--- a/test/functional/rpcbind_test.py
+++ b/test/functional/rpcbind_test.py
@@ -37,7 +37,7 @@ class RPCBindTest(BitcoinTestFramework):
base_args += ['-rpcallowip=' + x for x in allow_ips]
binds = ['-rpcbind='+addr for addr in addresses]
self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to)
- pid = bitcoind_processes[0].pid
+ pid = self.bitcoind_processes[0].pid
assert_equal(set(get_bind_addrs(pid)), set(expected))
self.stop_nodes()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index c7fd44b81c..8d698a7327 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -5,7 +5,9 @@
"""Base class for RPC testing."""
from collections import deque
+import errno
from enum import Enum
+import http.client
import logging
import optparse
import os
@@ -14,18 +16,17 @@ import subprocess
import sys
import tempfile
import time
+import traceback
+from .authproxy import JSONRPCException
+from . import coverage
from .util import (
- PortSeed,
MAX_NODES,
- bitcoind_processes,
+ PortSeed,
+ assert_equal,
check_json_precision,
connect_nodes_bi,
- disable_mocktime,
disconnect_nodes,
- enable_coverage,
- enable_mocktime,
- get_mocktime,
get_rpc_proxy,
initialize_datadir,
get_datadir_path,
@@ -33,15 +34,9 @@ from .util import (
p2p_port,
rpc_url,
set_node_times,
- _start_node,
- _start_nodes,
- _stop_node,
- _stop_nodes,
sync_blocks,
sync_mempools,
- wait_for_bitcoind_start,
)
-from .authproxy import JSONRPCException
class TestStatus(Enum):
PASSED = 1
@@ -52,6 +47,8 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
TEST_EXIT_SKIPPED = 77
+BITCOIND_PROC_WAIT_TIMEOUT = 60
+
class BitcoinTestFramework(object):
"""Base class for a bitcoin test script.
@@ -71,13 +68,15 @@ class BitcoinTestFramework(object):
def __init__(self):
self.num_nodes = 4
self.setup_clean_chain = False
- self.nodes = None
+ self.nodes = []
+ self.bitcoind_processes = {}
+ self.mocktime = 0
def add_options(self, parser):
pass
def setup_chain(self):
- self.log.info("Initializing test directory "+self.options.tmpdir)
+ self.log.info("Initializing test directory " + self.options.tmpdir)
if self.setup_clean_chain:
self._initialize_chain_clean(self.options.tmpdir, self.num_nodes)
else:
@@ -97,7 +96,7 @@ class BitcoinTestFramework(object):
extra_args = None
if hasattr(self, "extra_args"):
extra_args = self.extra_args
- self.nodes = _start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
def run_test(self):
raise NotImplementedError
@@ -111,9 +110,9 @@ class BitcoinTestFramework(object):
help="Leave bitcoinds and test.* datadir on exit or error")
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
help="Don't stop bitcoinds after the test execution")
- parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../../src"),
+ parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"),
help="Source directory containing bitcoind/bitcoin-cli (default: %default)")
- parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../cache"),
+ parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
help="Directory for caching pregenerated datadirs")
parser.add_option("--tmpdir", dest="tmpdir", help="Root directory for datadirs")
parser.add_option("-l", "--loglevel", dest="loglevel", default="INFO",
@@ -129,12 +128,9 @@ class BitcoinTestFramework(object):
self.add_options(parser)
(self.options, self.args) = parser.parse_args()
- if self.options.coveragedir:
- enable_coverage(self.options.coveragedir)
-
PortSeed.n = self.options.port_seed
- os.environ['PATH'] = self.options.srcdir+":"+self.options.srcdir+"/qt:"+os.environ['PATH']
+ os.environ['PATH'] = self.options.srcdir + ":" + self.options.srcdir + "/qt:" + os.environ['PATH']
check_json_precision()
@@ -188,7 +184,7 @@ class BitcoinTestFramework(object):
for fn in filenames:
try:
with open(fn, 'r') as f:
- print("From" , fn, ":")
+ print("From", fn, ":")
print("".join(deque(f, MAX_LINES_TO_PRINT)))
except OSError:
print("Opening file %s failed." % fn)
@@ -208,16 +204,88 @@ class BitcoinTestFramework(object):
# Public helper methods. These can be accessed by the subclass test scripts.
def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
- return _start_node(i, dirname, extra_args, rpchost, timewait, binary, stderr)
+ """Start a bitcoind and return RPC connection to it"""
+
+ datadir = os.path.join(dirname, "node" + str(i))
+ if binary is None:
+ binary = os.getenv("BITCOIND", "bitcoind")
+ args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(self.mocktime), "-uacomment=testnode%d" % i]
+ if extra_args is not None:
+ args.extend(extra_args)
+ self.bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
+ self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
+ self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i, rpchost)
+ self.log.debug("initialize_chain: RPC successfully started")
+ proxy = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, timeout=timewait)
+
+ if self.options.coveragedir:
+ coverage.write_all_rpc_commands(self.options.coveragedir, proxy)
+
+ return proxy
def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
- return _start_nodes(num_nodes, dirname, extra_args, rpchost, timewait, binary)
+ """Start multiple bitcoinds, return RPC connections to them"""
+
+ if extra_args is None:
+ extra_args = [None] * num_nodes
+ if binary is None:
+ binary = [None] * num_nodes
+ assert_equal(len(extra_args), num_nodes)
+ assert_equal(len(binary), num_nodes)
+ rpcs = []
+ try:
+ for i in range(num_nodes):
+ rpcs.append(self.start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
+ except:
+ # If one node failed to start, stop the others
+ # TODO: abusing self.nodes in this way is a little hacky.
+ # Eventually we should do a better job of tracking nodes
+ self.nodes.extend(rpcs)
+ self.stop_nodes()
+ self.nodes = []
+ raise
+ return rpcs
+
+ def stop_node(self, i):
+ """Stop a bitcoind test node"""
- def stop_node(self, num_node):
- _stop_node(self.nodes[num_node], num_node)
+ self.log.debug("Stopping node %d" % i)
+ try:
+ self.nodes[i].stop()
+ except http.client.CannotSendRequest as e:
+ self.log.exception("Unable to stop node")
+ return_code = self.bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
+ del self.bitcoind_processes[i]
+ assert_equal(return_code, 0)
def stop_nodes(self):
- _stop_nodes(self.nodes)
+ """Stop multiple bitcoind test nodes"""
+
+ for i in range(len(self.nodes)):
+ self.stop_node(i)
+ assert not self.bitcoind_processes.values() # All connections must be gone now
+
+ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_msg=None):
+ with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
+ try:
+ self.start_node(i, dirname, extra_args, stderr=log_stderr)
+ self.stop_node(i)
+ except Exception as e:
+ assert 'bitcoind exited' in str(e) # node must have shutdown
+ if expected_msg is not None:
+ log_stderr.seek(0)
+ stderr = log_stderr.read().decode('utf-8')
+ if expected_msg not in stderr:
+ raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr)
+ else:
+ if expected_msg is None:
+ assert_msg = "bitcoind should have exited with an error"
+ else:
+ assert_msg = "bitcoind should have exited with expected error " + expected_msg
+ raise AssertionError(assert_msg)
+
+ def wait_for_node_exit(self, i, timeout):
+ self.bitcoind_processes[i].wait(timeout)
def split_network(self):
"""
@@ -242,6 +310,21 @@ class BitcoinTestFramework(object):
sync_blocks(group)
sync_mempools(group)
+ def enable_mocktime(self):
+ """Enable mocktime for the script.
+
+ mocktime may be needed for scripts that use the cached version of the
+ blockchain. If the cached version of the blockchain is used without
+ mocktime then the mempools will not sync due to IBD.
+
+ For backwared compatibility of the python scripts with previous
+ versions of the cache, this helper function sets mocktime to Jan 1,
+ 2014 + (201 * 10 * 60)"""
+ self.mocktime = 1388534400 + (201 * 10 * 60)
+
+ def disable_mocktime(self):
+ self.mocktime = 0
+
# Private helper methods. These should not be accessed by the subclass test scripts.
def _start_logging(self):
@@ -257,7 +340,7 @@ class BitcoinTestFramework(object):
ll = int(self.options.loglevel) if self.options.loglevel.isdigit() else self.options.loglevel.upper()
ch.setLevel(ll)
# Format logs the same as bitcoind's debug.log with microprecision (so log files can be concatenated and sorted)
- formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)03d000 %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
+ formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d000 %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
formatter.converter = time.gmtime
fh.setFormatter(formatter)
ch.setFormatter(formatter)
@@ -299,9 +382,9 @@ class BitcoinTestFramework(object):
args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"]
if i > 0:
args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
- bitcoind_processes[i] = subprocess.Popen(args)
+ self.bitcoind_processes[i] = subprocess.Popen(args)
self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
- wait_for_bitcoind_start(bitcoind_processes[i], datadir, i)
+ self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i)
self.log.debug("initialize_chain: RPC successfully started")
self.nodes = []
@@ -319,8 +402,8 @@ class BitcoinTestFramework(object):
#
# blocks are created with timestamps 10 minutes apart
# starting from 2010 minutes in the past
- enable_mocktime()
- block_time = get_mocktime() - (201 * 10 * 60)
+ self.enable_mocktime()
+ block_time = self.mocktime - (201 * 10 * 60)
for i in range(2):
for peer in range(4):
for j in range(25):
@@ -333,7 +416,7 @@ class BitcoinTestFramework(object):
# Shut them down, and clean up cache directories:
self.stop_nodes()
self.nodes = []
- disable_mocktime()
+ self.disable_mocktime()
for i in range(MAX_NODES):
os.remove(log_filename(cachedir, i, "debug.log"))
os.remove(log_filename(cachedir, i, "db.log"))
@@ -354,18 +437,37 @@ class BitcoinTestFramework(object):
for i in range(num_nodes):
initialize_datadir(test_dir, i)
-# Test framework for doing p2p comparison testing, which sets up some bitcoind
-# binaries:
-# 1 binary: test binary
-# 2 binaries: 1 test binary, 1 ref binary
-# n>2 binaries: 1 test binary, n-1 ref binaries
-
-class SkipTest(Exception):
- """This exception is raised to skip a test"""
- def __init__(self, message):
- self.message = message
+ def _wait_for_bitcoind_start(self, process, datadir, i, rpchost=None):
+ """Wait for bitcoind to start.
+
+ This means that RPC is accessible and fully initialized.
+ Raise an exception if bitcoind exits during initialization."""
+ while True:
+ if process.poll() is not None:
+ raise Exception('bitcoind exited with status %i during initialization' % process.returncode)
+ try:
+ # Check if .cookie file to be created
+ rpc = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, coveragedir=self.options.coveragedir)
+ rpc.getblockcount()
+ break # break out of loop on success
+ except IOError as e:
+ if e.errno != errno.ECONNREFUSED: # Port not yet open?
+ raise # unknown IO error
+ except JSONRPCException as e: # Initialization phase
+ if e.error['code'] != -28: # RPC in warmup?
+ raise # unknown JSON RPC exception
+ except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
+ if "No RPC credentials" not in str(e):
+ raise
+ time.sleep(0.25)
class ComparisonTestFramework(BitcoinTestFramework):
+ """Test framework for doing p2p comparison testing
+
+ Sets up some bitcoind binaries:
+ - 1 binary: test binary
+ - 2 binaries: 1 test binary, 1 ref binary
+ - n>2 binaries: 1 test binary, n-1 ref binaries"""
def __init__(self):
super().__init__()
@@ -381,8 +483,15 @@ class ComparisonTestFramework(BitcoinTestFramework):
help="bitcoind binary to use for reference nodes (if any)")
def setup_network(self):
+ extra_args = [['-whitelist=127.0.0.1']]*self.num_nodes
+ if hasattr(self, "extra_args"):
+ extra_args = self.extra_args
self.nodes = self.start_nodes(
- self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1']] * self.num_nodes,
+ self.num_nodes, self.options.tmpdir, extra_args,
binary=[self.options.testbinary] +
- [self.options.refbinary]*(self.num_nodes-1))
+ [self.options.refbinary] * (self.num_nodes - 1))
+
+class SkipTest(Exception):
+ """This exception is raised to skip a test"""
+ def __init__(self, message):
+ self.message = message
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index fa6388bf96..acca72aa86 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -4,30 +4,162 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Helpful routines for regression testing."""
-import os
-import sys
-
-from binascii import hexlify, unhexlify
from base64 import b64encode
+from binascii import hexlify, unhexlify
from decimal import Decimal, ROUND_DOWN
import json
-import http.client
+import logging
+import os
import random
-import shutil
-import subprocess
-import tempfile
-import time
import re
-import errno
-import logging
+import time
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
-COVERAGE_DIR = None
-
logger = logging.getLogger("TestFramework.utils")
+# Assert functions
+##################
+
+def assert_fee_amount(fee, tx_size, fee_per_kB):
+ """Assert the fee was in range"""
+ target_fee = tx_size * fee_per_kB / 1000
+ if fee < target_fee:
+ raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
+ # allow the wallet's estimation to be at most 2 bytes off
+ if fee > (tx_size + 2) * fee_per_kB / 1000:
+ raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
+
+def assert_equal(thing1, thing2, *args):
+ if thing1 != thing2 or any(thing1 != arg for arg in args):
+ raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
+
+def assert_greater_than(thing1, thing2):
+ if thing1 <= thing2:
+ raise AssertionError("%s <= %s" % (str(thing1), str(thing2)))
+
+def assert_greater_than_or_equal(thing1, thing2):
+ if thing1 < thing2:
+ raise AssertionError("%s < %s" % (str(thing1), str(thing2)))
+
+def assert_raises(exc, fun, *args, **kwds):
+ assert_raises_message(exc, None, fun, *args, **kwds)
+
+def assert_raises_message(exc, message, fun, *args, **kwds):
+ try:
+ fun(*args, **kwds)
+ except exc as e:
+ if message is not None and message not in e.error['message']:
+ raise AssertionError("Expected substring not found:" + e.error['message'])
+ except Exception as e:
+ raise AssertionError("Unexpected exception raised: " + type(e).__name__)
+ else:
+ raise AssertionError("No exception raised")
+
+def assert_raises_jsonrpc(code, message, fun, *args, **kwds):
+ """Run an RPC and verify that a specific JSONRPC exception code and message is raised.
+
+ Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
+ and verifies that the error code and message are as expected. Throws AssertionError if
+ no JSONRPCException was returned or if the error code/message are not as expected.
+
+ Args:
+ code (int), optional: the error code returned by the RPC call (defined
+ in src/rpc/protocol.h). Set to None if checking the error code is not required.
+ message (string), optional: [a substring of] the error string returned by the
+ RPC call. Set to None if checking the error string is not required
+ fun (function): the function to call. This should be the name of an RPC.
+ args*: positional arguments for the function.
+ kwds**: named arguments for the function.
+ """
+ try:
+ fun(*args, **kwds)
+ except JSONRPCException as e:
+ # JSONRPCException was thrown as expected. Check the code and message values are correct.
+ if (code is not None) and (code != e.error["code"]):
+ raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
+ if (message is not None) and (message not in e.error['message']):
+ raise AssertionError("Expected substring not found:" + e.error['message'])
+ except Exception as e:
+ raise AssertionError("Unexpected exception raised: " + type(e).__name__)
+ else:
+ raise AssertionError("No exception raised")
+
+def assert_is_hex_string(string):
+ try:
+ int(string, 16)
+ except Exception as e:
+ raise AssertionError(
+ "Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
+
+def assert_is_hash_string(string, length=64):
+ if not isinstance(string, str):
+ raise AssertionError("Expected a string, got type %r" % type(string))
+ elif length and len(string) != length:
+ raise AssertionError(
+ "String of length %d expected; got %d" % (length, len(string)))
+ elif not re.match('[abcdef0-9]+$', string):
+ raise AssertionError(
+ "String %r contains invalid characters for a hash." % string)
+
+def assert_array_result(object_array, to_match, expected, should_not_find=False):
+ """
+ Pass in array of JSON objects, a dictionary with key/value pairs
+ to match against, and another dictionary with expected key/value
+ pairs.
+ If the should_not_find flag is true, to_match should not be found
+ in object_array
+ """
+ if should_not_find:
+ assert_equal(expected, {})
+ num_matched = 0
+ for item in object_array:
+ all_match = True
+ for key, value in to_match.items():
+ if item[key] != value:
+ all_match = False
+ if not all_match:
+ continue
+ elif should_not_find:
+ num_matched = num_matched + 1
+ for key, value in expected.items():
+ if item[key] != value:
+ raise AssertionError("%s : expected %s=%s" % (str(item), str(key), str(value)))
+ num_matched = num_matched + 1
+ if num_matched == 0 and not should_not_find:
+ raise AssertionError("No objects matched %s" % (str(to_match)))
+ if num_matched > 0 and should_not_find:
+ raise AssertionError("Objects were found %s" % (str(to_match)))
+
+# Utility functions
+###################
+
+def check_json_precision():
+ """Make sure json library being used does not lose precision converting BTC values"""
+ n = Decimal("20000000.00000003")
+ satoshis = int(json.loads(json.dumps(float(n))) * 1.0e8)
+ if satoshis != 2000000000000003:
+ raise RuntimeError("JSON encode/decode loses precision")
+
+def count_bytes(hex_string):
+ return len(bytearray.fromhex(hex_string))
+
+def bytes_to_hex_str(byte_str):
+ return hexlify(byte_str).decode('ascii')
+
+def hex_str_to_bytes(hex_str):
+ return unhexlify(hex_str.encode('ascii'))
+
+def str_to_b64str(string):
+ return b64encode(string.encode('utf-8')).decode('ascii')
+
+def satoshi_round(amount):
+ return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
+
+# RPC/P2P connection constants and functions
+############################################
+
# The maximum number of nodes a single test can spawn
MAX_NODES = 8
# Don't assign rpc or p2p ports lower than this
@@ -35,41 +167,11 @@ PORT_MIN = 11000
# The number of ports to "reserve" for p2p and rpc, each
PORT_RANGE = 5000
-BITCOIND_PROC_WAIT_TIMEOUT = 60
-
-
class PortSeed:
# Must be initialized with a unique integer for each process
n = None
-#Set Mocktime default to OFF.
-#MOCKTIME is only needed for scripts that use the
-#cached version of the blockchain. If the cached
-#version of the blockchain is used without MOCKTIME
-#then the mempools will not sync due to IBD.
-MOCKTIME = 0
-
-def enable_mocktime():
- #For backwared compatibility of the python scripts
- #with previous versions of the cache, set MOCKTIME
- #to Jan 1, 2014 + (201 * 10 * 60)
- global MOCKTIME
- MOCKTIME = 1388534400 + (201 * 10 * 60)
-
-def disable_mocktime():
- global MOCKTIME
- MOCKTIME = 0
-
-def get_mocktime():
- return MOCKTIME
-
-def enable_coverage(dirname):
- """Maintain a log of which RPC calls are made during testing."""
- global COVERAGE_DIR
- COVERAGE_DIR = dirname
-
-
-def get_rpc_proxy(url, node_number, timeout=None):
+def get_rpc_proxy(url, node_number, timeout=None, coveragedir=None):
"""
Args:
url (str): URL of the RPC server to call
@@ -90,11 +192,10 @@ def get_rpc_proxy(url, node_number, timeout=None):
proxy.url = url # store URL on proxy for info
coverage_logfile = coverage.get_filename(
- COVERAGE_DIR, node_number) if COVERAGE_DIR else None
+ coveragedir, node_number) if coveragedir else None
return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
-
def p2p_port(n):
assert(n <= MAX_NODES)
return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
@@ -102,106 +203,46 @@ def p2p_port(n):
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
-def check_json_precision():
- """Make sure json library being used does not lose precision converting BTC values"""
- n = Decimal("20000000.00000003")
- satoshis = int(json.loads(json.dumps(float(n)))*1.0e8)
- if satoshis != 2000000000000003:
- raise RuntimeError("JSON encode/decode loses precision")
-
-def count_bytes(hex_string):
- return len(bytearray.fromhex(hex_string))
-
-def bytes_to_hex_str(byte_str):
- return hexlify(byte_str).decode('ascii')
-
-def hex_str_to_bytes(hex_str):
- return unhexlify(hex_str.encode('ascii'))
-
-def str_to_b64str(string):
- return b64encode(string.encode('utf-8')).decode('ascii')
-
-def sync_blocks(rpc_connections, *, wait=1, timeout=60):
- """
- Wait until everybody has the same tip.
-
- sync_blocks needs to be called with an rpc_connections set that has least
- one node already synced to the latest, stable tip, otherwise there's a
- chance it might return before all nodes are stably synced.
- """
- # Use getblockcount() instead of waitforblockheight() to determine the
- # initial max height because the two RPCs look at different internal global
- # variables (chainActive vs latestBlock) and the former gets updated
- # earlier.
- maxheight = max(x.getblockcount() for x in rpc_connections)
- start_time = cur_time = time.time()
- while cur_time <= start_time + timeout:
- tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections]
- if all(t["height"] == maxheight for t in tips):
- if all(t["hash"] == tips[0]["hash"] for t in tips):
- return
- raise AssertionError("Block sync failed, mismatched block hashes:{}".format(
- "".join("\n {!r}".format(tip) for tip in tips)))
- cur_time = time.time()
- raise AssertionError("Block sync to height {} timed out:{}".format(
- maxheight, "".join("\n {!r}".format(tip) for tip in tips)))
-
-def sync_chain(rpc_connections, *, wait=1, timeout=60):
- """
- Wait until everybody has the same best block
- """
- while timeout > 0:
- best_hash = [x.getbestblockhash() for x in rpc_connections]
- if best_hash == [best_hash[0]]*len(best_hash):
- return
- time.sleep(wait)
- timeout -= wait
- raise AssertionError("Chain sync failed: Best block hashes don't match")
-
-def sync_mempools(rpc_connections, *, wait=1, timeout=60):
- """
- Wait until everybody has the same transactions in their memory
- pools
- """
- while timeout > 0:
- pool = set(rpc_connections[0].getrawmempool())
- num_match = 1
- for i in range(1, len(rpc_connections)):
- if set(rpc_connections[i].getrawmempool()) == pool:
- num_match = num_match+1
- if num_match == len(rpc_connections):
- return
- time.sleep(wait)
- timeout -= wait
- raise AssertionError("Mempool sync failed")
+def rpc_url(datadir, i, rpchost=None):
+ rpc_u, rpc_p = get_auth_cookie(datadir, i)
+ host = '127.0.0.1'
+ port = rpc_port(i)
+ if rpchost:
+ parts = rpchost.split(':')
+ if len(parts) == 2:
+ host, port = parts
+ else:
+ host = rpchost
+ return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
-bitcoind_processes = {}
+# Node functions
+################
def initialize_datadir(dirname, n):
- datadir = os.path.join(dirname, "node"+str(n))
+ datadir = os.path.join(dirname, "node" + str(n))
if not os.path.isdir(datadir):
os.makedirs(datadir)
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
f.write("regtest=1\n")
- f.write("port="+str(p2p_port(n))+"\n")
- f.write("rpcport="+str(rpc_port(n))+"\n")
+ f.write("port=" + str(p2p_port(n)) + "\n")
+ f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("listenonion=0\n")
return datadir
def get_datadir_path(dirname, n):
- return os.path.join(dirname, "node"+str(n))
+ return os.path.join(dirname, "node" + str(n))
def get_auth_cookie(datadir, n):
user = None
password = None
if os.path.isfile(os.path.join(datadir, "bitcoin.conf")):
- with open(os.path.join(datadir, "bitcoin.conf"), 'r') as f:
+ with open(os.path.join(datadir, "bitcoin.conf"), 'r', encoding='utf8') as f:
for line in f:
if line.startswith("rpcuser="):
- assert user is None # Ensure that there is only one rpcuser line
+ assert user is None # Ensure that there is only one rpcuser line
user = line.split("=")[1].strip("\n")
if line.startswith("rpcpassword="):
- assert password is None # Ensure that there is only one rpcpassword line
+ assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
with open(os.path.join(datadir, "regtest", ".cookie"), 'r') as f:
@@ -213,126 +254,12 @@ def get_auth_cookie(datadir, n):
raise ValueError("No RPC credentials")
return user, password
-def rpc_url(datadir, i, rpchost=None):
- rpc_u, rpc_p = get_auth_cookie(datadir, i)
- host = '127.0.0.1'
- port = rpc_port(i)
- if rpchost:
- parts = rpchost.split(':')
- if len(parts) == 2:
- host, port = parts
- else:
- host = rpchost
- return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
-
-def wait_for_bitcoind_start(process, datadir, i, rpchost=None):
- '''
- Wait for bitcoind to start. This means that RPC is accessible and fully initialized.
- Raise an exception if bitcoind exits during initialization.
- '''
- while True:
- if process.poll() is not None:
- raise Exception('bitcoind exited with status %i during initialization' % process.returncode)
- try:
- # Check if .cookie file to be created
- rpc = get_rpc_proxy(rpc_url(datadir, i, rpchost), i)
- blocks = rpc.getblockcount()
- break # break out of loop on success
- except IOError as e:
- if e.errno != errno.ECONNREFUSED: # Port not yet open?
- raise # unknown IO error
- except JSONRPCException as e: # Initialization phase
- if e.error['code'] != -28: # RPC in warmup?
- raise # unknown JSON RPC exception
- except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
- if "No RPC credentials" not in str(e):
- raise
- time.sleep(0.25)
-
-
-def _start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
- """Start a bitcoind and return RPC connection to it
-
- This function should only be called from within test_framework, not by individual test scripts."""
-
- datadir = os.path.join(dirname, "node"+str(i))
- if binary is None:
- binary = os.getenv("BITCOIND", "bitcoind")
- args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(get_mocktime()), "-uacomment=testnode%d" % i]
- if extra_args is not None: args.extend(extra_args)
- bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
- logger.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
- wait_for_bitcoind_start(bitcoind_processes[i], datadir, i, rpchost)
- logger.debug("initialize_chain: RPC successfully started")
- proxy = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, timeout=timewait)
-
- if COVERAGE_DIR:
- coverage.write_all_rpc_commands(COVERAGE_DIR, proxy)
-
- return proxy
-
-def assert_start_raises_init_error(i, dirname, extra_args=None, expected_msg=None):
- with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
- try:
- node = _start_node(i, dirname, extra_args, stderr=log_stderr)
- _stop_node(node, i)
- except Exception as e:
- assert 'bitcoind exited' in str(e) #node must have shutdown
- if expected_msg is not None:
- log_stderr.seek(0)
- stderr = log_stderr.read().decode('utf-8')
- if expected_msg not in stderr:
- raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr)
- else:
- if expected_msg is None:
- assert_msg = "bitcoind should have exited with an error"
- else:
- assert_msg = "bitcoind should have exited with expected error " + expected_msg
- raise AssertionError(assert_msg)
-
-def _start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
- """Start multiple bitcoinds, return RPC connections to them
-
- This function should only be called from within test_framework, not by individual test scripts."""
-
- if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
- if binary is None: binary = [ None for _ in range(num_nodes) ]
- assert_equal(len(extra_args), num_nodes)
- assert_equal(len(binary), num_nodes)
- rpcs = []
- try:
- for i in range(num_nodes):
- rpcs.append(_start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
- except: # If one node failed to start, stop the others
- _stop_nodes(rpcs)
- raise
- return rpcs
-
def log_filename(dirname, n_node, logname):
- return os.path.join(dirname, "node"+str(n_node), "regtest", logname)
-
-def _stop_node(node, i):
- """Stop a bitcoind test node
-
- This function should only be called from within test_framework, not by individual test scripts."""
-
- logger.debug("Stopping node %d" % i)
- try:
- node.stop()
- except http.client.CannotSendRequest as e:
- logger.exception("Unable to stop node")
- return_code = bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
- del bitcoind_processes[i]
- assert_equal(return_code, 0)
-
-def _stop_nodes(nodes):
- """Stop multiple bitcoind test nodes
-
- This function should only be called from within test_framework, not by individual test scripts."""
+ return os.path.join(dirname, "node" + str(n_node), "regtest", logname)
- for i, node in enumerate(nodes):
- _stop_node(node, i)
- assert not bitcoind_processes.values() # All connections must be gone now
+def get_bip9_status(node, key):
+ info = node.getblockchaininfo()
+ return info['bip9_softforks'][key]
def set_node_times(nodes, t):
for node in nodes:
@@ -350,7 +277,7 @@ def disconnect_nodes(from_connection, node_num):
raise AssertionError("timed out waiting for disconnect")
def connect_nodes(from_connection, node_num):
- ip_port = "127.0.0.1:"+str(p2p_port(node_num))
+ ip_port = "127.0.0.1:" + str(p2p_port(node_num))
from_connection.addnode(ip_port, "onetry")
# poll until version handshake complete to avoid race conditions
# with transaction relaying
@@ -361,6 +288,63 @@ def connect_nodes_bi(nodes, a, b):
connect_nodes(nodes[a], b)
connect_nodes(nodes[b], a)
+def sync_blocks(rpc_connections, *, wait=1, timeout=60):
+ """
+ Wait until everybody has the same tip.
+
+ sync_blocks needs to be called with an rpc_connections set that has least
+ one node already synced to the latest, stable tip, otherwise there's a
+ chance it might return before all nodes are stably synced.
+ """
+ # Use getblockcount() instead of waitforblockheight() to determine the
+ # initial max height because the two RPCs look at different internal global
+ # variables (chainActive vs latestBlock) and the former gets updated
+ # earlier.
+ maxheight = max(x.getblockcount() for x in rpc_connections)
+ start_time = cur_time = time.time()
+ while cur_time <= start_time + timeout:
+ tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections]
+ if all(t["height"] == maxheight for t in tips):
+ if all(t["hash"] == tips[0]["hash"] for t in tips):
+ return
+ raise AssertionError("Block sync failed, mismatched block hashes:{}".format(
+ "".join("\n {!r}".format(tip) for tip in tips)))
+ cur_time = time.time()
+ raise AssertionError("Block sync to height {} timed out:{}".format(
+ maxheight, "".join("\n {!r}".format(tip) for tip in tips)))
+
+def sync_chain(rpc_connections, *, wait=1, timeout=60):
+ """
+ Wait until everybody has the same best block
+ """
+ while timeout > 0:
+ best_hash = [x.getbestblockhash() for x in rpc_connections]
+ if best_hash == [best_hash[0]] * len(best_hash):
+ return
+ time.sleep(wait)
+ timeout -= wait
+ raise AssertionError("Chain sync failed: Best block hashes don't match")
+
+def sync_mempools(rpc_connections, *, wait=1, timeout=60):
+ """
+ Wait until everybody has the same transactions in their memory
+ pools
+ """
+ while timeout > 0:
+ pool = set(rpc_connections[0].getrawmempool())
+ num_match = 1
+ for i in range(1, len(rpc_connections)):
+ if set(rpc_connections[i].getrawmempool()) == pool:
+ num_match = num_match + 1
+ if num_match == len(rpc_connections):
+ return
+ time.sleep(wait)
+ timeout -= wait
+ raise AssertionError("Mempool sync failed")
+
+# Transaction/Block functions
+#############################
+
def find_output(node, txid, amount):
"""
Return index to output of txid with value amount
@@ -370,14 +354,13 @@ def find_output(node, txid, amount):
for i in range(len(txdata["vout"])):
if txdata["vout"][i]["value"] == amount:
return i
- raise RuntimeError("find_output txid %s : %s not found"%(txid,str(amount)))
-
+ raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
def gather_inputs(from_node, amount_needed, confirmations_required=1):
"""
Return a random set of unspent txouts that are enough to pay amount_needed
"""
- assert(confirmations_required >=0)
+ assert(confirmations_required >= 0)
utxo = from_node.listunspent(confirmations_required)
random.shuffle(utxo)
inputs = []
@@ -385,9 +368,9 @@ def gather_inputs(from_node, amount_needed, confirmations_required=1):
while total_in < amount_needed and len(utxo) > 0:
t = utxo.pop()
total_in += t["amount"]
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"], "address" : t["address"] } )
+ inputs.append({"txid": t["txid"], "vout": t["vout"], "address": t["address"]})
if total_in < amount_needed:
- raise RuntimeError("Insufficient funds: need %d, have %d"%(amount_needed, total_in))
+ raise RuntimeError("Insufficient funds: need %d, have %d" % (amount_needed, total_in))
return (total_in, inputs)
def make_change(from_node, amount_in, amount_out, fee):
@@ -395,13 +378,13 @@ def make_change(from_node, amount_in, amount_out, fee):
Create change output(s), return them
"""
outputs = {}
- amount = amount_out+fee
+ amount = amount_out + fee
change = amount_in - amount
- if change > amount*2:
+ if change > amount * 2:
# Create an extra change output to break up big inputs
change_address = from_node.getnewaddress()
# Split change in two, being careful of rounding:
- outputs[change_address] = Decimal(change/2).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
+ outputs[change_address] = Decimal(change / 2).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
change = amount_in - amount - outputs[change_address]
if change > 0:
outputs[from_node.getnewaddress()] = change
@@ -414,9 +397,9 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
"""
from_node = random.choice(nodes)
to_node = random.choice(nodes)
- fee = min_fee + fee_increment*random.randint(0,fee_variants)
+ fee = min_fee + fee_increment * random.randint(0, fee_variants)
- (total_in, inputs) = gather_inputs(from_node, amount+fee)
+ (total_in, inputs) = gather_inputs(from_node, amount + fee)
outputs = make_change(from_node, total_in, amount, fee)
outputs[to_node.getnewaddress()] = float(amount)
@@ -426,123 +409,13 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
return (txid, signresult["hex"], fee)
-def assert_fee_amount(fee, tx_size, fee_per_kB):
- """Assert the fee was in range"""
- target_fee = tx_size * fee_per_kB / 1000
- if fee < target_fee:
- raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)"%(str(fee), str(target_fee)))
- # allow the wallet's estimation to be at most 2 bytes off
- if fee > (tx_size + 2) * fee_per_kB / 1000:
- raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee)))
-
-def assert_equal(thing1, thing2, *args):
- if thing1 != thing2 or any(thing1 != arg for arg in args):
- raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
-
-def assert_greater_than(thing1, thing2):
- if thing1 <= thing2:
- raise AssertionError("%s <= %s"%(str(thing1),str(thing2)))
-
-def assert_greater_than_or_equal(thing1, thing2):
- if thing1 < thing2:
- raise AssertionError("%s < %s"%(str(thing1),str(thing2)))
-
-def assert_raises(exc, fun, *args, **kwds):
- assert_raises_message(exc, None, fun, *args, **kwds)
-
-def assert_raises_message(exc, message, fun, *args, **kwds):
- try:
- fun(*args, **kwds)
- except exc as e:
- if message is not None and message not in e.error['message']:
- raise AssertionError("Expected substring not found:"+e.error['message'])
- except Exception as e:
- raise AssertionError("Unexpected exception raised: "+type(e).__name__)
- else:
- raise AssertionError("No exception raised")
-
-def assert_raises_jsonrpc(code, message, fun, *args, **kwds):
- """Run an RPC and verify that a specific JSONRPC exception code and message is raised.
-
- Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
- and verifies that the error code and message are as expected. Throws AssertionError if
- no JSONRPCException was returned or if the error code/message are not as expected.
-
- Args:
- code (int), optional: the error code returned by the RPC call (defined
- in src/rpc/protocol.h). Set to None if checking the error code is not required.
- message (string), optional: [a substring of] the error string returned by the
- RPC call. Set to None if checking the error string is not required
- fun (function): the function to call. This should be the name of an RPC.
- args*: positional arguments for the function.
- kwds**: named arguments for the function.
- """
- try:
- fun(*args, **kwds)
- except JSONRPCException as e:
- # JSONRPCException was thrown as expected. Check the code and message values are correct.
- if (code is not None) and (code != e.error["code"]):
- raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
- if (message is not None) and (message not in e.error['message']):
- raise AssertionError("Expected substring not found:"+e.error['message'])
- except Exception as e:
- raise AssertionError("Unexpected exception raised: "+type(e).__name__)
- else:
- raise AssertionError("No exception raised")
-
-def assert_is_hex_string(string):
- try:
- int(string, 16)
- except Exception as e:
- raise AssertionError(
- "Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
-
-def assert_is_hash_string(string, length=64):
- if not isinstance(string, str):
- raise AssertionError("Expected a string, got type %r" % type(string))
- elif length and len(string) != length:
- raise AssertionError(
- "String of length %d expected; got %d" % (length, len(string)))
- elif not re.match('[abcdef0-9]+$', string):
- raise AssertionError(
- "String %r contains invalid characters for a hash." % string)
-
-def assert_array_result(object_array, to_match, expected, should_not_find = False):
- """
- Pass in array of JSON objects, a dictionary with key/value pairs
- to match against, and another dictionary with expected key/value
- pairs.
- If the should_not_find flag is true, to_match should not be found
- in object_array
- """
- if should_not_find == True:
- assert_equal(expected, { })
- num_matched = 0
- for item in object_array:
- all_match = True
- for key,value in to_match.items():
- if item[key] != value:
- all_match = False
- if not all_match:
- continue
- elif should_not_find == True:
- num_matched = num_matched+1
- for key,value in expected.items():
- if item[key] != value:
- raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
- num_matched = num_matched+1
- if num_matched == 0 and should_not_find != True:
- raise AssertionError("No objects matched %s"%(str(to_match)))
- if num_matched > 0 and should_not_find == True:
- raise AssertionError("Objects were found %s"%(str(to_match)))
-
-def satoshi_round(amount):
- return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
-
# Helper to create at least "count" utxos
# Pass in a fee that is sufficient for relay and mining new transactions.
def create_confirmed_utxos(fee, node, count):
- node.generate(int(0.5*count)+101)
+ to_generate = int(0.5 * count) + 101
+ while to_generate > 0:
+ node.generate(min(25, to_generate))
+ to_generate -= 25
utxos = node.listunspent()
iterations = count - len(utxos)
addr1 = node.getnewaddress()
@@ -552,14 +425,14 @@ def create_confirmed_utxos(fee, node, count):
for i in range(iterations):
t = utxos.pop()
inputs = []
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
+ inputs.append({"txid": t["txid"], "vout": t["vout"]})
outputs = {}
send_value = t['amount'] - fee
- outputs[addr1] = satoshi_round(send_value/2)
- outputs[addr2] = satoshi_round(send_value/2)
+ outputs[addr1] = satoshi_round(send_value / 2)
+ outputs[addr2] = satoshi_round(send_value / 2)
raw_tx = node.createrawtransaction(inputs, outputs)
signed_tx = node.signrawtransaction(raw_tx)["hex"]
- txid = node.sendrawtransaction(signed_tx)
+ node.sendrawtransaction(signed_tx)
while (node.getmempoolinfo()['size'] > 0):
node.generate(1)
@@ -574,8 +447,8 @@ def gen_return_txouts():
# Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
# So we have big transactions (and therefore can't fit very many into each block)
# create one script_pubkey
- script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes
- for i in range (512):
+ script_pubkey = "6a4d0200" # OP_RETURN OP_PUSH2 512 bytes
+ for i in range(512):
script_pubkey = script_pubkey + "01"
# concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
txouts = "81"
@@ -589,8 +462,8 @@ def gen_return_txouts():
return txouts
def create_tx(node, coinbase, to_address, amount):
- inputs = [{ "txid" : coinbase, "vout" : 0}]
- outputs = { to_address : amount }
+ inputs = [{"txid": coinbase, "vout": 0}]
+ outputs = {to_address: amount}
rawtx = node.createrawtransaction(inputs, outputs)
signresult = node.signrawtransaction(rawtx)
assert_equal(signresult["complete"], True)
@@ -603,7 +476,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
txids = []
for _ in range(num):
t = utxos.pop()
- inputs=[{ "txid" : t["txid"], "vout" : t["vout"]}]
+ inputs = [{"txid": t["txid"], "vout": t["vout"]}]
outputs = {}
change = t['amount'] - fee
outputs[addr] = satoshi_round(change)
@@ -628,7 +501,3 @@ def mine_large_block(node, utxos=None):
fee = 100 * node.getnetworkinfo()["relayfee"]
create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
node.generate(1)
-
-def get_bip9_status(node, key):
- info = node.getblockchaininfo()
- return info['bip9_softforks'][key]
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index d411534ca2..b7bc6e841b 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -126,6 +126,7 @@ EXTENDED_SCRIPTS = [
# vv Tests less than 5m vv
'maxuploadtarget.py',
'mempool_packages.py',
+ 'dbcrash.py',
# vv Tests less than 2m vv
'bip68-sequence.py',
'getblocktemplate_longpoll.py',
diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py
index 9cb32d4650..569cc46e6c 100755
--- a/test/functional/wallet-dump.py
+++ b/test/functional/wallet-dump.py
@@ -7,7 +7,7 @@
import os
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (assert_equal, bitcoind_processes)
+from test_framework.util import assert_equal
def read_dump(file_name, addrs, hd_master_addr_old):
@@ -95,7 +95,7 @@ class WalletDumpTest(BitcoinTestFramework):
#encrypt wallet, restart, unlock and dump
self.nodes[0].encryptwallet('test')
- bitcoind_processes[0].wait()
+ self.bitcoind_processes[0].wait()
self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0])
self.nodes[0].walletpassphrase('test', 10)
# Should be a no-op:
diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py
index 33872e3c94..ba72918fe1 100755
--- a/test/functional/wallet-encryption.py
+++ b/test/functional/wallet-encryption.py
@@ -6,12 +6,10 @@
import time
-from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT
from test_framework.util import (
assert_equal,
assert_raises_jsonrpc,
- bitcoind_processes,
- BITCOIND_PROC_WAIT_TIMEOUT,
)
class WalletEncryptionTest(BitcoinTestFramework):
@@ -33,7 +31,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
# Encrypt the wallet
self.nodes[0].encryptwallet(passphrase)
- bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
+ self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
self.nodes[0] = self.start_node(0, self.options.tmpdir)
# Test that the wallet is encrypted
diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py
index e7ec72a248..dfd3dc83c5 100755
--- a/test/functional/wallet-hd.py
+++ b/test/functional/wallet-hd.py
@@ -8,7 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
- assert_start_raises_init_error
)
import os
import shutil
@@ -27,7 +26,7 @@ class WalletHDTest(BitcoinTestFramework):
# Make sure can't switch off usehd after wallet creation
self.stop_node(1)
- assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet')
+ self.assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet')
self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py
index a4507182a2..ff51cba4b3 100755
--- a/test/functional/walletbackup.py
+++ b/test/functional/walletbackup.py
@@ -30,10 +30,11 @@ confirm 1/2/3/4 balances are same as before.
Shutdown again, restore using importwallet,
and confirm again balances are correct.
"""
+from random import randint
+import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from random import randint
class WalletBackupTest(BitcoinTestFramework):