aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am15
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/addrman.cpp8
-rw-r--r--src/addrman.h11
-rw-r--r--src/amount.h45
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bench/checkqueue.cpp2
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/crypto_hash.cpp25
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/bitcoin-tx.cpp8
-rw-r--r--src/chainparams.cpp38
-rw-r--r--src/chainparams.h16
-rw-r--r--src/chainparamsbase.cpp24
-rw-r--r--src/chainparamsbase.h16
-rw-r--r--src/checkqueue.h2
-rw-r--r--src/coins.h5
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/crypto/chacha20.cpp180
-rw-r--r--src/crypto/chacha20.h26
-rw-r--r--src/crypto/common.h21
-rw-r--r--src/httprpc.cpp4
-rw-r--r--src/httpserver.cpp40
-rw-r--r--src/httpserver.h4
-rw-r--r--src/init.cpp92
-rw-r--r--src/key.cpp8
-rw-r--r--src/key.h3
-rw-r--r--src/memusage.h20
-rw-r--r--src/merkleblock.cpp2
-rw-r--r--src/merkleblock.h2
-rw-r--r--src/miner.cpp5
-rw-r--r--src/net.cpp136
-rw-r--r--src/net.h15
-rw-r--r--src/net_processing.cpp353
-rw-r--r--src/net_processing.h8
-rw-r--r--src/netaddress.cpp6
-rw-r--r--src/netaddress.h1
-rw-r--r--src/policy/feerate.cpp (renamed from src/amount.cpp)2
-rw-r--r--src/policy/feerate.h54
-rw-r--r--src/policy/fees.cpp209
-rw-r--r--src/policy/fees.h130
-rw-r--r--src/policy/policy.cpp40
-rw-r--r--src/policy/policy.h6
-rw-r--r--src/primitives/transaction.h41
-rw-r--r--src/qt/bantablemodel.cpp4
-rw-r--r--src/qt/bitcoingui.cpp3
-rw-r--r--src/qt/clientmodel.cpp34
-rw-r--r--src/qt/clientmodel.h6
-rw-r--r--src/qt/coincontroldialog.cpp12
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/paymentserver.cpp12
-rw-r--r--src/qt/peertablemodel.cpp2
-rw-r--r--src/qt/rpcconsole.cpp6
-rw-r--r--src/qt/sendcoinsdialog.cpp7
-rw-r--r--src/qt/test/wallettests.cpp3
-rw-r--r--src/qt/transactionrecord.cpp10
-rw-r--r--src/qt/walletmodel.cpp1
-rw-r--r--src/random.cpp88
-rw-r--r--src/random.h78
-rw-r--r--src/rest.cpp9
-rw-r--r--src/rpc/blockchain.cpp74
-rw-r--r--src/rpc/client.cpp4
-rw-r--r--src/rpc/mining.cpp14
-rw-r--r--src/rpc/misc.cpp72
-rw-r--r--src/rpc/net.cpp34
-rw-r--r--src/rpc/rawtransaction.cpp78
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/script/sigcache.cpp22
-rw-r--r--src/script/sigcache.h21
-rw-r--r--src/serialize.h11
-rw-r--r--src/streams.h8
-rw-r--r--src/support/cleanse.h1
-rw-r--r--src/test/addrman_tests.cpp204
-rw-r--r--src/test/amount_tests.cpp39
-rw-r--r--src/test/crypto_tests.cpp68
-rw-r--r--src/test/cuckoocache_tests.cpp25
-rw-r--r--src/test/main_tests.cpp7
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/test/net_tests.cpp13
-rw-r--r--src/test/policyestimator_tests.cpp45
-rw-r--r--src/test/pow_tests.cpp32
-rw-r--r--src/test/prevector_tests.cpp8
-rw-r--r--src/test/random_tests.cpp36
-rw-r--r--src/test/test_bitcoin.cpp3
-rw-r--r--src/test/test_random.h8
-rw-r--r--src/test/util_tests.cpp77
-rw-r--r--src/test/versionbits_tests.cpp6
-rw-r--r--src/txmempool.cpp64
-rw-r--r--src/txmempool.h17
-rw-r--r--src/util.cpp69
-rw-r--r--src/util.h74
-rw-r--r--src/utiltime.cpp12
-rw-r--r--src/validation.cpp232
-rw-r--r--src/validation.h22
-rw-r--r--src/validationinterface.cpp18
-rw-r--r--src/validationinterface.h38
-rw-r--r--src/wallet/coincontrol.h1
-rw-r--r--src/wallet/db.cpp135
-rw-r--r--src/wallet/db.h103
-rw-r--r--src/wallet/feebumper.cpp11
-rw-r--r--src/wallet/rpcdump.cpp27
-rw-r--r--src/wallet/rpcwallet.cpp50
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp3
-rw-r--r--src/wallet/test/wallet_tests.cpp13
-rw-r--r--src/wallet/wallet.cpp564
-rw-r--r--src/wallet/wallet.h123
-rw-r--r--src/wallet/walletdb.cpp113
-rw-r--r--src/wallet/walletdb.h39
-rw-r--r--src/zmq/zmqnotificationinterface.cpp22
-rw-r--r--src/zmq/zmqnotificationinterface.h7
-rw-r--r--src/zmq/zmqpublishnotifier.cpp1
112 files changed, 2774 insertions, 1821 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 69b5bb48ad..14d55a944f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -116,6 +116,7 @@ BITCOIN_CORE_H = \
netbase.h \
netmessagemaker.h \
noui.h \
+ policy/feerate.h \
policy/fees.h \
policy/policy.h \
policy/rbf.h \
@@ -178,8 +179,8 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
- addrman.cpp \
addrdb.cpp \
+ addrman.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
@@ -246,6 +247,8 @@ crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
+ crypto/chacha20.h \
+ crypto/chacha20.cpp \
crypto/common.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
@@ -299,7 +302,6 @@ libbitcoin_consensus_a_SOURCES = \
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
- amount.cpp \
base58.cpp \
chainparams.cpp \
coins.cpp \
@@ -310,6 +312,7 @@ libbitcoin_common_a_SOURCES = \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
+ policy/feerate.cpp \
protocol.cpp \
scheduler.cpp \
script/sign.cpp \
@@ -462,6 +465,14 @@ DISTCLEANFILES = obj/build.h
EXTRA_DIST = $(CTAES_DIST)
+
+config/bitcoin-config.h: config/stamp-h1
+ @$(MAKE) -C $(top_builddir) $(subdir)/$(@)
+config/stamp-h1: $(top_srcdir)/$(subdir)/config/bitcoin-config.h.in $(top_builddir)/config.status
+ $(AM_V_at)$(MAKE) -C $(top_builddir) $(subdir)/$(@)
+$(top_srcdir)/$(subdir)/config/bitcoin-config.h.in: $(am__configure_deps)
+ $(AM_V_at)$(MAKE) -C $(top_srcdir) $(subdir)/config/bitcoin-config.h.in
+
clean-local:
-$(MAKE) -C secp256k1 clean
-$(MAKE) -C univalue clean
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index fe0ed59fe2..10cb7e775a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -57,8 +57,8 @@ BITCOIN_TESTS =\
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prevector_tests.cpp \
- test/random_tests.cpp \
test/raii_event_tests.cpp \
+ test/random_tests.cpp \
test/reverselock_tests.cpp \
test/rpc_tests.cpp \
test/sanity_tests.cpp \
@@ -97,7 +97,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
- $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS)
+ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index ed9c128eb2..4a408b9beb 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -351,8 +351,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
+ nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
+ nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
@@ -368,8 +368,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
+ nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
+ nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
diff --git a/src/addrman.h b/src/addrman.h
index f123b20b1b..70d907488f 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -136,13 +136,13 @@ public:
*/
//! total number of buckets for tried addresses
-#define ADDRMAN_TRIED_BUCKET_COUNT 256
+#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8
//! total number of buckets for new addresses
-#define ADDRMAN_NEW_BUCKET_COUNT 1024
+#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10
//! maximum allowed number of entries in buckets for new and tried addresses
-#define ADDRMAN_BUCKET_SIZE 64
+#define ADDRMAN_BUCKET_SIZE_LOG2 6
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
@@ -171,6 +171,11 @@ public:
//! the maximum number of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX 2500
+//! Convenience
+#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
+#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
+#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
+
/**
* Stochastical (IP) address manager
*/
diff --git a/src/amount.h b/src/amount.h
index 93060f7193..2bd367cba2 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -6,10 +6,7 @@
#ifndef BITCOIN_AMOUNT_H
#define BITCOIN_AMOUNT_H
-#include "serialize.h"
-
-#include <stdlib.h>
-#include <string>
+#include <stdint.h>
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;
@@ -17,8 +14,6 @@ typedef int64_t CAmount;
static const CAmount COIN = 100000000;
static const CAmount CENT = 1000000;
-extern const std::string CURRENCY_UNIT;
-
/** No amount larger than this (in satoshi) is valid.
*
* Note that this constant is *not* the total money supply, which in Bitcoin
@@ -31,42 +26,4 @@ extern const std::string CURRENCY_UNIT;
static const CAmount MAX_MONEY = 21000000 * COIN;
inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
-/**
- * Fee rate in satoshis per kilobyte: CAmount / kB
- */
-class CFeeRate
-{
-private:
- CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
-public:
- /** Fee rate of 0 satoshis per kB */
- CFeeRate() : nSatoshisPerK(0) { }
- explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
- /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/
- CFeeRate(const CAmount& nFeePaid, size_t nBytes);
- CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; }
- /**
- * Return the fee in satoshis for the given size in bytes.
- */
- CAmount GetFee(size_t nBytes) const;
- /**
- * Return the fee in satoshis for a size of 1000 bytes
- */
- CAmount GetFeePerK() const { return GetFee(1000); }
- friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
- friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
- friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
- friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
- friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
- CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
- std::string ToString() const;
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(nSatoshisPerK);
- }
-};
-
#endif // BITCOIN_AMOUNT_H
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index c6c932454a..195388839e 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -40,7 +40,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
char a = '\0';
stream.write(&a, 1); // Prevent compaction
- Consensus::Params params = Params(CBaseChainParams::MAIN).GetConsensus();
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
while (state.KeepRunning()) {
CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
@@ -48,7 +48,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
assert(stream.Rewind(sizeof(block_bench::block413567)));
CValidationState validationState;
- assert(CheckBlock(block, validationState, params));
+ assert(CheckBlock(block, validationState, chainParams->GetConsensus()));
}
}
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 6fa9fe4fe8..88a2a570f9 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -68,7 +68,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
PrevectorJob(){
}
PrevectorJob(FastRandomContext& insecure_rand){
- p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE*2));
+ p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
}
bool operator()()
{
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 06882f1514..42891f345b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -48,7 +48,7 @@ static void CoinSelection(benchmark::State& state)
addCoin(1000 * COIN, wallet, vCoins);
addCoin(3 * COIN, wallet, vCoins);
- std::set<std::pair<const CWalletTx*, unsigned int> > setCoinsRet;
+ std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet);
assert(success);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 737d3572ae..2914a36c7b 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -7,6 +7,7 @@
#include "bench.h"
#include "bloom.h"
#include "hash.h"
+#include "random.h"
#include "uint256.h"
#include "utiltime.h"
#include "crypto/ripemd160.h"
@@ -69,6 +70,28 @@ static void SipHash_32b(benchmark::State& state)
}
}
+static void FastRandom_32bit(benchmark::State& state)
+{
+ FastRandomContext rng(true);
+ uint32_t x = 0;
+ while (state.KeepRunning()) {
+ for (int i = 0; i < 1000000; i++) {
+ x += rng.rand32();
+ }
+ }
+}
+
+static void FastRandom_1bit(benchmark::State& state)
+{
+ FastRandomContext rng(true);
+ uint32_t x = 0;
+ while (state.KeepRunning()) {
+ for (int i = 0; i < 1000000; i++) {
+ x += rng.randbool();
+ }
+ }
+}
+
BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
@@ -76,3 +99,5 @@ BENCHMARK(SHA512);
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
+BENCHMARK(FastRandom_32bit);
+BENCHMARK(FastRandom_1bit);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 5edd43d41e..885b787b4d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -30,6 +30,8 @@ static const int CONTINUE_EXECUTION=-1;
std::string HelpMessageCli()
{
+ const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
+ const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
std::string strUsage;
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
@@ -38,7 +40,7 @@ std::string HelpMessageCli()
AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
- strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
+ strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 83b855cbcf..45738b5df8 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -657,11 +657,13 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
- else if (command == "outpubkey")
+ else if (command == "outpubkey") {
+ if (!ecc) { ecc.reset(new Secp256k1Init()); }
MutateTxAddOutPubKey(tx, commandVal);
- else if (command == "outmultisig")
+ } else if (command == "outmultisig") {
+ if (!ecc) { ecc.reset(new Secp256k1Init()); }
MutateTxAddOutMultiSig(tx, commandVal);
- else if (command == "outscript")
+ } else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
else if (command == "outdata")
MutateTxAddOutData(tx, commandVal);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 1dc29826af..5055fb3e0a 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -55,6 +55,12 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}
+void CChainParams::UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+{
+ consensus.vDeployments[d].nStartTime = nStartTime;
+ consensus.vDeployments[d].nTimeout = nTimeout;
+}
+
/**
* Main network
*/
@@ -165,7 +171,6 @@ public:
};
}
};
-static CMainParams mainParams;
/**
* Testnet (v3)
@@ -253,7 +258,6 @@ public:
}
};
-static CTestNetParams testNetParams;
/**
* Regression test
@@ -326,42 +330,34 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();
}
-
- void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
- {
- consensus.vDeployments[d].nStartTime = nStartTime;
- consensus.vDeployments[d].nTimeout = nTimeout;
- }
};
-static CRegTestParams regTestParams;
-static CChainParams *pCurrentParams = 0;
+static std::unique_ptr<CChainParams> globalChainParams;
const CChainParams &Params() {
- assert(pCurrentParams);
- return *pCurrentParams;
+ assert(globalChainParams);
+ return *globalChainParams;
}
-CChainParams& Params(const std::string& chain)
+std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
- return mainParams;
+ return std::unique_ptr<CChainParams>(new CMainParams());
else if (chain == CBaseChainParams::TESTNET)
- return testNetParams;
+ return std::unique_ptr<CChainParams>(new CTestNetParams());
else if (chain == CBaseChainParams::REGTEST)
- return regTestParams;
- else
- throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
+ return std::unique_ptr<CChainParams>(new CRegTestParams());
+ throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
void SelectParams(const std::string& network)
{
SelectBaseParams(network);
- pCurrentParams = &Params(network);
+ globalChainParams = CreateChainParams(network);
}
-void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
{
- regTestParams.UpdateBIP9Parameters(d, nStartTime, nTimeout);
+ globalChainParams->UpdateBIP9Parameters(d, nStartTime, nTimeout);
}
diff --git a/src/chainparams.h b/src/chainparams.h
index 4fe88c691c..e5312d1080 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -11,6 +11,7 @@
#include "primitives/block.h"
#include "protocol.h"
+#include <memory>
#include <vector>
struct CDNSSeedData {
@@ -75,6 +76,7 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
+ void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
protected:
CChainParams() {}
@@ -95,15 +97,17 @@ protected:
};
/**
- * Return the currently selected parameters. This won't change after app
- * startup, except for unit tests.
+ * Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
+ * @returns a CChainParams* of the chosen chain.
+ * @throws a std::runtime_error if the chain is not supported.
*/
-const CChainParams &Params();
+std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain);
/**
- * @returns CChainParams for the given BIP70 chain name.
+ * Return the currently selected parameters. This won't change after app
+ * startup, except for unit tests.
*/
-CChainParams& Params(const std::string& chain);
+const CChainParams &Params();
/**
* Sets the params returned by Params() to those for the given BIP70 chain name.
@@ -114,6 +118,6 @@ void SelectParams(const std::string& chain);
/**
* Allows modifying the BIP9 regtest parameters.
*/
-void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
+void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index cb71a8b550..43c9a13c54 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -35,7 +35,6 @@ public:
nRPCPort = 8332;
}
};
-static CBaseMainParams mainParams;
/**
* Testnet (v3)
@@ -49,7 +48,6 @@ public:
strDataDir = "testnet3";
}
};
-static CBaseTestNetParams testNetParams;
/*
* Regression test
@@ -63,31 +61,30 @@ public:
strDataDir = "regtest";
}
};
-static CBaseRegTestParams regTestParams;
-static CBaseChainParams* pCurrentBaseParams = 0;
+static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
const CBaseChainParams& BaseParams()
{
- assert(pCurrentBaseParams);
- return *pCurrentBaseParams;
+ assert(globalChainBaseParams);
+ return *globalChainBaseParams;
}
-CBaseChainParams& BaseParams(const std::string& chain)
+std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
- return mainParams;
+ return std::unique_ptr<CBaseChainParams>(new CBaseMainParams());
else if (chain == CBaseChainParams::TESTNET)
- return testNetParams;
+ return std::unique_ptr<CBaseChainParams>(new CBaseTestNetParams());
else if (chain == CBaseChainParams::REGTEST)
- return regTestParams;
+ return std::unique_ptr<CBaseChainParams>(new CBaseRegTestParams());
else
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
void SelectBaseParams(const std::string& chain)
{
- pCurrentBaseParams = &BaseParams(chain);
+ globalChainBaseParams = CreateBaseChainParams(chain);
}
std::string ChainNameFromCommandLine()
@@ -103,8 +100,3 @@ std::string ChainNameFromCommandLine()
return CBaseChainParams::TESTNET;
return CBaseChainParams::MAIN;
}
-
-bool AreBaseParamsConfigured()
-{
- return pCurrentBaseParams != NULL;
-}
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 59493afb9b..fc101f5b77 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_CHAINPARAMSBASE_H
#define BITCOIN_CHAINPARAMSBASE_H
+#include <memory>
#include <string>
#include <vector>
@@ -31,6 +32,13 @@ protected:
};
/**
+ * Creates and returns a std::unique_ptr<CBaseChainParams> of the chosen chain.
+ * @returns a CBaseChainParams* of the chosen chain.
+ * @throws a std::runtime_error if the chain is not supported.
+ */
+std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain);
+
+/**
* Append the help messages for the chainparams options to the
* parameter string.
*/
@@ -42,8 +50,6 @@ void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp=true);
*/
const CBaseChainParams& BaseParams();
-CBaseChainParams& BaseParams(const std::string& chain);
-
/** Sets the params returned by Params() to those for the given network. */
void SelectBaseParams(const std::string& chain);
@@ -53,10 +59,4 @@ void SelectBaseParams(const std::string& chain);
*/
std::string ChainNameFromCommandLine();
-/**
- * Return true if SelectBaseParamsFromCommandLine() has been called to select
- * a network.
- */
-bool AreBaseParamsConfigured();
-
#endif // BITCOIN_CHAINPARAMSBASE_H
diff --git a/src/checkqueue.h b/src/checkqueue.h
index ea12df66dd..63c104c02a 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_CHECKQUEUE_H
#define BITCOIN_CHECKQUEUE_H
+#include "sync.h"
+
#include <algorithm>
#include <vector>
diff --git a/src/coins.h b/src/coins.h
index 8ee49b33ae..065bae56e9 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_COINS_H
#define BITCOIN_COINS_H
+#include "primitives/transaction.h"
#include "compressor.h"
#include "core_memusage.h"
#include "hash.h"
@@ -17,7 +18,7 @@
#include <stdint.h>
#include <boost/foreach.hpp>
-#include <boost/unordered_map.hpp>
+#include <unordered_map>
/**
* Pruned version of CTransaction: only retains metadata and unspent transaction outputs
@@ -279,7 +280,7 @@ struct CCoinsCacheEntry
CCoinsCacheEntry() : coins(), flags(0) {}
};
-typedef boost::unordered_map<uint256, CCoinsCacheEntry, SaltedTxidHasher> CCoinsMap;
+typedef std::unordered_map<uint256, CCoinsCacheEntry, SaltedTxidHasher> CCoinsMap;
/** Cursor for iterating over CoinsView state */
class CCoinsViewCursor
diff --git a/src/core_write.cpp b/src/core_write.cpp
index a3ca87c8b5..d116e617ee 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -151,6 +151,8 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
entry.pushKV("version", tx.nVersion);
+ entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
+ entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
new file mode 100644
index 0000000000..816ae870e1
--- /dev/null
+++ b/src/crypto/chacha20.cpp
@@ -0,0 +1,180 @@
+// 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.
+
+// Based on the public domain implementation 'merged' by D. J. Bernstein
+// See https://cr.yp.to/chacha.html.
+
+#include "crypto/common.h"
+#include "crypto/chacha20.h"
+
+#include <string.h>
+
+constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); }
+
+#define QUARTERROUND(a,b,c,d) \
+ a += b; d = rotl32(d ^ a, 16); \
+ c += d; b = rotl32(b ^ c, 12); \
+ a += b; d = rotl32(d ^ a, 8); \
+ c += d; b = rotl32(b ^ c, 7);
+
+static const unsigned char sigma[] = "expand 32-byte k";
+static const unsigned char tau[] = "expand 16-byte k";
+
+void ChaCha20::SetKey(const unsigned char* k, size_t keylen)
+{
+ const unsigned char *constants;
+
+ input[4] = ReadLE32(k + 0);
+ input[5] = ReadLE32(k + 4);
+ input[6] = ReadLE32(k + 8);
+ input[7] = ReadLE32(k + 12);
+ if (keylen == 32) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* keylen == 16 */
+ constants = tau;
+ }
+ input[8] = ReadLE32(k + 0);
+ input[9] = ReadLE32(k + 4);
+ input[10] = ReadLE32(k + 8);
+ input[11] = ReadLE32(k + 12);
+ input[0] = ReadLE32(constants + 0);
+ input[1] = ReadLE32(constants + 4);
+ input[2] = ReadLE32(constants + 8);
+ input[3] = ReadLE32(constants + 12);
+ input[12] = 0;
+ input[13] = 0;
+ input[14] = 0;
+ input[15] = 0;
+}
+
+ChaCha20::ChaCha20()
+{
+ memset(input, 0, sizeof(input));
+}
+
+ChaCha20::ChaCha20(const unsigned char* k, size_t keylen)
+{
+ SetKey(k, keylen);
+}
+
+void ChaCha20::SetIV(uint64_t iv)
+{
+ input[14] = iv;
+ input[15] = iv >> 32;
+}
+
+void ChaCha20::Seek(uint64_t pos)
+{
+ input[12] = pos;
+ input[13] = pos >> 32;
+}
+
+void ChaCha20::Output(unsigned char* c, size_t bytes)
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ unsigned char *ctarget = NULL;
+ unsigned char tmp[64];
+ unsigned int i;
+
+ if (!bytes) return;
+
+ j0 = input[0];
+ j1 = input[1];
+ j2 = input[2];
+ j3 = input[3];
+ j4 = input[4];
+ j5 = input[5];
+ j6 = input[6];
+ j7 = input[7];
+ j8 = input[8];
+ j9 = input[9];
+ j10 = input[10];
+ j11 = input[11];
+ j12 = input[12];
+ j13 = input[13];
+ j14 = input[14];
+ j15 = input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 += j0;
+ x1 += j1;
+ x2 += j2;
+ x3 += j3;
+ x4 += j4;
+ x5 += j5;
+ x6 += j6;
+ x7 += j7;
+ x8 += j8;
+ x9 += j9;
+ x10 += j10;
+ x11 += j11;
+ x12 += j12;
+ x13 += j13;
+ x14 += j14;
+ x15 += j15;
+
+ ++j12;
+ if (!j12) ++j13;
+
+ WriteLE32(c + 0, x0);
+ WriteLE32(c + 4, x1);
+ WriteLE32(c + 8, x2);
+ WriteLE32(c + 12, x3);
+ WriteLE32(c + 16, x4);
+ WriteLE32(c + 20, x5);
+ WriteLE32(c + 24, x6);
+ WriteLE32(c + 28, x7);
+ WriteLE32(c + 32, x8);
+ WriteLE32(c + 36, x9);
+ WriteLE32(c + 40, x10);
+ WriteLE32(c + 44, x11);
+ WriteLE32(c + 48, x12);
+ WriteLE32(c + 52, x13);
+ WriteLE32(c + 56, x14);
+ WriteLE32(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ input[12] = j12;
+ input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ }
+}
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
new file mode 100644
index 0000000000..a305977bcd
--- /dev/null
+++ b/src/crypto/chacha20.h
@@ -0,0 +1,26 @@
+// 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_CRYPTO_CHACHA20_H
+#define BITCOIN_CRYPTO_CHACHA20_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** A PRNG class for ChaCha20. */
+class ChaCha20
+{
+private:
+ uint32_t input[16];
+
+public:
+ ChaCha20();
+ ChaCha20(const unsigned char* key, size_t keylen);
+ void SetKey(const unsigned char* key, size_t keylen);
+ void SetIV(uint64_t iv);
+ void Seek(uint64_t pos);
+ void Output(unsigned char* output, size_t bytes);
+};
+
+#endif // BITCOIN_CRYPTO_CHACHA20_H
diff --git a/src/crypto/common.h b/src/crypto/common.h
index 4a9d1150b6..bcca3d30ea 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -79,4 +79,25 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
memcpy(ptr, (char*)&v, 8);
}
+/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */
+uint64_t static inline CountBits(uint64_t x)
+{
+#ifdef HAVE_DECL___BUILTIN_CLZL
+ if (sizeof(unsigned long) >= sizeof(uint64_t)) {
+ return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
+ }
+#endif
+#ifdef HAVE_DECL___BUILTIN_CLZLL
+ if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
+ return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
+ }
+#endif
+ int ret = 0;
+ while (x) {
+ x >>= 1;
+ ++ret;
+ }
+ return ret;
+}
+
#endif // BITCOIN_CRYPTO_COMMON_H
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 21c64c5c83..18a9819edd 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -93,9 +93,9 @@ static bool multiUserAuthorized(std::string strUserPass)
std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
- if (mapMultiArgs.count("-rpcauth") > 0) {
+ if (gArgs.IsArgSet("-rpcauth")) {
//Search for multi-user login/pass "rpcauth" from config
- BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs.at("-rpcauth"))
+ BOOST_FOREACH(std::string strRPCAuth, gArgs.GetArgs("-rpcauth"))
{
std::vector<std::string> vFields;
boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 347433eb11..0d1cba3fd2 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -196,9 +196,8 @@ 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 (mapMultiArgs.count("-rpcallowip")) {
- const std::vector<std::string>& vAllow = mapMultiArgs.at("-rpcallowip");
- for (std::string strAllow : vAllow) {
+ if (gArgs.IsArgSet("-rpcallowip")) {
+ for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
CSubNet subnet;
LookupSubNet(strAllow.c_str(), subnet);
if (!subnet.IsValid()) {
@@ -321,12 +320,11 @@ static bool HTTPBindAddresses(struct evhttp* http)
if (IsArgSet("-rpcbind")) {
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
}
- } else if (mapMultiArgs.count("-rpcbind")) { // Specific bind address
- const std::vector<std::string>& vbind = mapMultiArgs.at("-rpcbind");
- for (std::vector<std::string>::const_iterator i = vbind.begin(); i != vbind.end(); ++i) {
+ } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
+ for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
int port = defaultPort;
std::string host;
- SplitHostPort(*i, port, host);
+ SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port));
}
} else { // No specific bind address specified, bind to any
@@ -384,15 +382,13 @@ bool InitHTTPServer()
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
-#if LIBEVENT_VERSION_NUMBER >= 0x02010100
- // If -debug=libevent, set full libevent debugging.
- // Otherwise, disable all libevent debugging.
- if (LogAcceptCategory(BCLog::LIBEVENT)) {
- event_enable_debug_logging(EVENT_DBG_ALL);
- } else {
- event_enable_debug_logging(EVENT_DBG_NONE);
+ // Update libevent's log handling. Returns false if our version of
+ // libevent doesn't support debug logging, in which case we should
+ // clear the BCLog::LIBEVENT flag.
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
}
-#endif
+
#ifdef WIN32
evthread_use_windows_threads();
#else
@@ -435,6 +431,20 @@ bool InitHTTPServer()
return true;
}
+bool UpdateHTTPServerLogging(bool enable) {
+#if LIBEVENT_VERSION_NUMBER >= 0x02010100
+ if (enable) {
+ event_enable_debug_logging(EVENT_DBG_ALL);
+ } else {
+ event_enable_debug_logging(EVENT_DBG_NONE);
+ }
+ return true;
+#else
+ // Can't update libevent logging if version < 02010100
+ return false;
+#endif
+}
+
std::thread threadHTTP;
std::future<bool> threadResult;
diff --git a/src/httpserver.h b/src/httpserver.h
index b55b253bea..6be9950682 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -32,6 +32,10 @@ void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
+/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
+ * libevent doesn't support debug logging.*/
+bool UpdateHTTPServerLogging(bool enable);
+
/** Handler for requests to a certain HTTP path */
typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
diff --git a/src/init.cpp b/src/init.cpp
index 1e7e388a52..93b4c80c01 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -25,6 +25,8 @@
#include "netbase.h"
#include "net.h"
#include "net_processing.h"
+#include "policy/feerate.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "rpc/server.h"
#include "rpc/register.h"
@@ -207,15 +209,16 @@ void Shutdown()
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
- if (fDumpMempoolLater)
+ if (fDumpMempoolLater && GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
+ }
if (fFeeEstimatesInitialized)
{
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION);
if (!est_fileout.IsNull())
- mempool.WriteFeeEstimates(est_fileout);
+ ::feeEstimator.Write(est_fileout);
else
LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
fFeeEstimatesInitialized = false;
@@ -326,6 +329,10 @@ void OnRPCPreCommand(const CRPCCommand& cmd)
std::string HelpMessage(HelpMessageMode mode)
{
+ const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
+ const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
+ const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET);
const bool showDebug = GetBoolArg("-help-debug", false);
// When adding new options to the categories, please keep and ensure alphabetical ordering.
@@ -337,7 +344,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
- strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), Params(CBaseChainParams::MAIN).GetConsensus().defaultAssumeValid.GetHex(), Params(CBaseChainParams::TESTNET).GetConsensus().defaultAssumeValid.GetHex()));
+ strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
if (mode == HMM_BITCOIND)
{
@@ -353,6 +360,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
+ strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
@@ -390,7 +398,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG));
strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS));
- strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort()));
+ strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()));
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
@@ -427,14 +435,16 @@ std::string HelpMessage(HelpMessageMode mode)
{
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
- strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
- strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
+ strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
+ strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages");
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT));
+ strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT));
+
strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT));
strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
@@ -467,7 +477,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug) {
- strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard()));
+ strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard()));
strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
@@ -495,7 +505,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times"));
- strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
+ strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS));
@@ -676,8 +686,10 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
StartShutdown();
}
} // End scope of CImportingNow
- LoadMempool();
- fDumpMempoolLater = !fRequestShutdown;
+ if (GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ LoadMempool();
+ fDumpMempoolLater = !fRequestShutdown;
+ }
}
/** Sanity checks
@@ -734,7 +746,7 @@ void InitParameterInteraction()
LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
}
- if (mapMultiArgs.count("-connect") && mapMultiArgs.at("-connect").size() > 0) {
+ if (gArgs.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
if (SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
@@ -888,8 +900,8 @@ bool AppInitParameterInteraction()
// Make sure enough file descriptors are available
int nBind = std::max(
- (mapMultiArgs.count("-bind") ? mapMultiArgs.at("-bind").size() : 0) +
- (mapMultiArgs.count("-whitebind") ? mapMultiArgs.at("-whitebind").size() : 0), size_t(1));
+ (gArgs.IsArgSet("-bind") ? gArgs.GetArgs("-bind").size() : 0) +
+ (gArgs.IsArgSet("-whitebind") ? gArgs.GetArgs("-whitebind").size() : 0), size_t(1));
nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
@@ -904,9 +916,9 @@ bool AppInitParameterInteraction()
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
- if (mapMultiArgs.count("-debug") > 0) {
+ if (gArgs.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
- const std::vector<std::string>& categories = mapMultiArgs.at("-debug");
+ const std::vector<std::string> categories = gArgs.GetArgs("-debug");
if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
for (const auto& cat : categories) {
@@ -921,9 +933,8 @@ bool AppInitParameterInteraction()
}
// Now remove the logging categories which were explicitly excluded
- if (mapMultiArgs.count("-debugexclude") > 0) {
- const std::vector<std::string>& excludedCategories = mapMultiArgs.at("-debugexclude");
- for (const auto& cat : excludedCategories) {
+ 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));
@@ -1093,15 +1104,14 @@ bool AppInitParameterInteraction()
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
- if (mapMultiArgs.count("-bip9params")) {
+ if (gArgs.IsArgSet("-bip9params")) {
// Allow overriding BIP9 parameters for testing
if (!chainparams.MineBlocksOnDemand()) {
return InitError("BIP9 parameters may only be overridden on regtest.");
}
- const std::vector<std::string>& deployments = mapMultiArgs.at("-bip9params");
- for (auto i : deployments) {
+ for (const std::string& strDeployment : gArgs.GetArgs("-bip9params")) {
std::vector<std::string> vDeploymentParams;
- boost::split(vDeploymentParams, i, boost::is_any_of(":"));
+ boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3) {
return InitError("BIP9 parameters malformed, expecting deployment:start:end");
}
@@ -1116,7 +1126,7 @@ bool AppInitParameterInteraction()
for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
{
if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
- UpdateRegtestBIP9Parameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
+ UpdateBIP9Parameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
found = true;
LogPrintf("Setting BIP9 activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
break;
@@ -1247,8 +1257,8 @@ 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 (mapMultiArgs.count("-uacomment")) {
- BOOST_FOREACH(std::string cmt, mapMultiArgs.at("-uacomment"))
+ if (gArgs.IsArgSet("-uacomment")) {
+ BOOST_FOREACH(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));
@@ -1261,9 +1271,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
- if (mapMultiArgs.count("-onlynet")) {
+ if (gArgs.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
- BOOST_FOREACH(const std::string& snet, mapMultiArgs.at("-onlynet")) {
+ BOOST_FOREACH(const std::string& snet, gArgs.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
@@ -1276,8 +1286,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (mapMultiArgs.count("-whitelist")) {
- BOOST_FOREACH(const std::string& net, mapMultiArgs.at("-whitelist")) {
+ if (gArgs.IsArgSet("-whitelist")) {
+ BOOST_FOREACH(const std::string& net, gArgs.GetArgs("-whitelist")) {
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
@@ -1338,16 +1348,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
if (fListen) {
bool fBound = false;
- if (mapMultiArgs.count("-bind")) {
- BOOST_FOREACH(const std::string& strBind, mapMultiArgs.at("-bind")) {
+ if (gArgs.IsArgSet("-bind")) {
+ BOOST_FOREACH(const std::string& strBind, gArgs.GetArgs("-bind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(ResolveErrMsg("bind", strBind));
fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
}
- if (mapMultiArgs.count("-whitebind")) {
- BOOST_FOREACH(const std::string& strBind, mapMultiArgs.at("-whitebind")) {
+ if (gArgs.IsArgSet("-whitebind")) {
+ BOOST_FOREACH(const std::string& strBind, gArgs.GetArgs("-whitebind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false))
return InitError(ResolveErrMsg("whitebind", strBind));
@@ -1356,7 +1366,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
}
}
- if (!mapMultiArgs.count("-bind") && !mapMultiArgs.count("-whitebind")) {
+ if (!gArgs.IsArgSet("-bind") && !gArgs.IsArgSet("-whitebind")) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE);
@@ -1366,8 +1376,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(_("Failed to listen on any port. Use -listen=0 if you want this."));
}
- if (mapMultiArgs.count("-externalip")) {
- BOOST_FOREACH(const std::string& strAddr, mapMultiArgs.at("-externalip")) {
+ if (gArgs.IsArgSet("-externalip")) {
+ BOOST_FOREACH(const std::string& strAddr, gArgs.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
@@ -1376,8 +1386,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (mapMultiArgs.count("-seednode")) {
- BOOST_FOREACH(const std::string& strDest, mapMultiArgs.at("-seednode"))
+ if (gArgs.IsArgSet("-seednode")) {
+ BOOST_FOREACH(const std::string& strDest, gArgs.GetArgs("-seednode"))
connman.AddOneShot(strDest);
}
@@ -1550,7 +1560,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull())
- mempool.ReadFeeEstimates(est_filein);
+ ::feeEstimator.Read(est_filein);
fFeeEstimatesInitialized = true;
// ********************************************************* Step 8: load wallet
@@ -1603,9 +1613,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
std::vector<fs::path> vImportFiles;
- if (mapMultiArgs.count("-loadblock"))
+ if (gArgs.IsArgSet("-loadblock"))
{
- BOOST_FOREACH(const std::string& strFile, mapMultiArgs.at("-loadblock"))
+ BOOST_FOREACH(const std::string& strFile, gArgs.GetArgs("-loadblock"))
vImportFiles.push_back(strFile);
}
diff --git a/src/key.cpp b/src/key.cpp
index b4f0dc8202..5a75647f1a 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -131,14 +131,6 @@ void CKey::MakeNewKey(bool fCompressedIn) {
fCompressed = fCompressedIn;
}
-bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
- if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size()))
- return false;
- fCompressed = fCompressedIn;
- fValid = true;
- return true;
-}
-
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
diff --git a/src/key.h b/src/key.h
index 925a8d186d..2c6f151727 100644
--- a/src/key.h
+++ b/src/key.h
@@ -94,9 +94,6 @@ public:
//! Check whether the public key corresponding to this private key is (to be) compressed.
bool IsCompressed() const { return fCompressed; }
- //! Initialize from a CPrivKey (serialized OpenSSL private key data).
- bool SetPrivKey(const CPrivKey& vchPrivKey, bool fCompressed);
-
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
diff --git a/src/memusage.h b/src/memusage.h
index 81e8702954..b69acafffd 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -12,6 +12,8 @@
#include <map>
#include <set>
#include <vector>
+#include <unordered_map>
+#include <unordered_set>
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
@@ -149,7 +151,7 @@ static inline size_t DynamicUsage(const std::shared_ptr<X>& p)
// Boost data structures
template<typename X>
-struct boost_unordered_node : private X
+struct unordered_node : private X
{
private:
void* ptr;
@@ -158,13 +160,25 @@ private:
template<typename X, typename Y>
static inline size_t DynamicUsage(const boost::unordered_set<X, Y>& s)
{
- return MallocUsage(sizeof(boost_unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
+ return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
}
template<typename X, typename Y, typename Z>
static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m)
{
- return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
+ return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
+}
+
+template<typename X, typename Y>
+static inline size_t DynamicUsage(const std::unordered_set<X, Y>& s)
+{
+ return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
+}
+
+template<typename X, typename Y, typename Z>
+static inline size_t DynamicUsage(const std::unordered_map<X, Y, Z>& m)
+{
+ return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
}
}
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index e3f3e4621a..78d7cd6001 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -65,7 +65,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve
} else {
// calculate left hash
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
- // calculate right hash if not beyond the end of the array - copy left hash otherwise1
+ // calculate right hash if not beyond the end of the array - copy left hash otherwise
if (pos*2+1 < CalcTreeWidth(height-1))
right = CalcHash(height-1, pos*2+1, vTxid);
else
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 73cbf670ee..de4c5c8d29 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -23,7 +23,7 @@
* storing a bit for each traversed node, signifying whether the node is the
* parent of at least one matched leaf txid (or a matched txid itself). In
* case we are at the leaf level, or this bit is 0, its merkle node hash is
- * stored, and its children are not explorer further. Otherwise, no hash is
+ * stored, and its children are not explored further. Otherwise, no hash is
* stored, but we recurse into both (or the only) child branch. During
* decoding, the same depth-first traversal is performed, consuming bits and
* hashes as they written during encoding.
diff --git a/src/miner.cpp b/src/miner.cpp
index 386d75c4be..9d2959723a 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -15,6 +15,7 @@
#include "hash.h"
#include "validation.h"
#include "net.h"
+#include "policy/feerate.h"
#include "policy/policy.h"
#include "pow.h"
#include "primitives/transaction.h"
@@ -317,9 +318,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already
bool BlockAssembler::SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx)
{
assert (it != mempool.mapTx.end());
- if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it))
- return true;
- return false;
+ return mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it);
}
void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, CTxMemPool::txiter entry, std::vector<CTxMemPool::txiter>& sortedEntries)
diff --git a/src/net.cpp b/src/net.cpp
index cf94faf854..dd375e580f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -418,10 +418,10 @@ void CConnman::DumpBanlist()
CBanDB bandb;
banmap_t banmap;
- SetBannedSetDirty(false);
GetBanned(banmap);
- if (!bandb.Write(banmap))
- SetBannedSetDirty(true);
+ if (bandb.Write(banmap)) {
+ SetBannedSetDirty(false);
+ }
LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
@@ -541,6 +541,8 @@ bool CConnman::Unban(const CSubNet &subNet) {
void CConnman::GetBanned(banmap_t &banMap)
{
LOCK(cs_setBanned);
+ // Sweep the banlist so expired bans are not returned
+ SweepBanned();
banMap = setBanned; //create a thread safe copy
}
@@ -708,7 +710,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
handled = msg.readData(pch, nBytes);
if (handled < 0)
- return false;
+ return false;
if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
LogPrint(BCLog::NET, "Oversized message from peer=%i, disconnecting\n", GetId());
@@ -786,7 +788,7 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes)
// reject messages larger than MAX_SIZE
if (hdr.nMessageSize > MAX_SIZE)
- return -1;
+ return -1;
// switch state to reading message data
in_data = true;
@@ -946,7 +948,7 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
- NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime,
+ NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
(node->nServices & nRelevantServices) == nRelevantServices,
node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup};
@@ -1299,59 +1301,55 @@ void CConnman::ThreadSocketHandler()
}
if (recvSet || errorSet)
{
+ // typical socket buffer is 8K-64K
+ char pchBuf[0x10000];
+ int nBytes = 0;
{
- {
- // typical socket buffer is 8K-64K
- char pchBuf[0x10000];
- int nBytes = 0;
- {
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
- nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
- }
- if (nBytes > 0)
- {
- bool notify = false;
- if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
- pnode->CloseSocketDisconnect();
- RecordBytesRecv(nBytes);
- if (notify) {
- size_t nSizeAdded = 0;
- auto it(pnode->vRecvMsg.begin());
- for (; it != pnode->vRecvMsg.end(); ++it) {
- if (!it->complete())
- break;
- nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
- }
- {
- LOCK(pnode->cs_vProcessMsg);
- pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
- pnode->nProcessQueueSize += nSizeAdded;
- pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
- }
- WakeMessageHandler();
- }
- }
- else if (nBytes == 0)
- {
- // socket closed gracefully
- if (!pnode->fDisconnect) {
- LogPrint(BCLog::NET, "socket closed\n");
- }
- pnode->CloseSocketDisconnect();
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
+ nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
+ }
+ if (nBytes > 0)
+ {
+ bool notify = false;
+ if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
+ pnode->CloseSocketDisconnect();
+ RecordBytesRecv(nBytes);
+ if (notify) {
+ size_t nSizeAdded = 0;
+ auto it(pnode->vRecvMsg.begin());
+ for (; it != pnode->vRecvMsg.end(); ++it) {
+ if (!it->complete())
+ break;
+ nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
}
- else if (nBytes < 0)
{
- // error
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
- {
- if (!pnode->fDisconnect)
- LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
- pnode->CloseSocketDisconnect();
- }
+ LOCK(pnode->cs_vProcessMsg);
+ pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
+ pnode->nProcessQueueSize += nSizeAdded;
+ pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
}
+ WakeMessageHandler();
+ }
+ }
+ else if (nBytes == 0)
+ {
+ // socket closed gracefully
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "socket closed\n");
+ }
+ pnode->CloseSocketDisconnect();
+ }
+ else if (nBytes < 0)
+ {
+ // error
+ int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
+ {
+ if (!pnode->fDisconnect)
+ LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
+ pnode->CloseSocketDisconnect();
}
}
}
@@ -1376,7 +1374,7 @@ void CConnman::ThreadSocketHandler()
{
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
- LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);
+ LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
@@ -1396,7 +1394,7 @@ void CConnman::ThreadSocketHandler()
}
else if (!pnode->fSuccessfullyConnected)
{
- LogPrintf("version handshake timeout from %d\n", pnode->id);
+ LogPrintf("version handshake timeout from %d\n", pnode->GetId());
pnode->fDisconnect = true;
}
}
@@ -1585,6 +1583,9 @@ void CConnman::ThreadDNSAddressSeed()
LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) {
+ if (interruptNet) {
+ return;
+ }
if (HaveNameProxy()) {
AddOneShot(seed.host);
} else {
@@ -1602,6 +1603,9 @@ void CConnman::ThreadDNSAddressSeed()
found++;
}
}
+ if (interruptNet) {
+ return;
+ }
// TODO: The seed name resolve may fail, yielding an IP of [::], which results in
// addrman assigning the same source to results from different seeds.
// This should switch to a hard-coded stable dummy IP for each seed name, so that the
@@ -1666,12 +1670,12 @@ void CConnman::ProcessOneShot()
void CConnman::ThreadOpenConnections()
{
// Connect to specific addresses
- if (mapMultiArgs.count("-connect") && mapMultiArgs.at("-connect").size() > 0)
+ if (gArgs.IsArgSet("-connect") && gArgs.GetArgs("-connect").size() > 0)
{
for (int64_t nLoop = 0;; nLoop++)
{
ProcessOneShot();
- BOOST_FOREACH(const std::string& strAddr, mapMultiArgs.at("-connect"))
+ BOOST_FOREACH(const std::string& strAddr, gArgs.GetArgs("-connect"))
{
CAddress addr(CService(), NODE_NONE);
OpenNetworkConnection(addr, false, NULL, strAddr.c_str());
@@ -1873,8 +1877,8 @@ void CConnman::ThreadOpenAddedConnections()
{
{
LOCK(cs_vAddedNodes);
- if (mapMultiArgs.count("-addnode"))
- vAddedNodes = mapMultiArgs.at("-addnode");
+ if (gArgs.IsArgSet("-addnode"))
+ vAddedNodes = gArgs.GetArgs("-addnode");
}
while (true)
@@ -2285,7 +2289,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));
// Initiate outbound connections unless connect=0
- if (!mapMultiArgs.count("-connect") || mapMultiArgs.at("-connect").size() != 1 || mapMultiArgs.at("-connect")[0] != "0")
+ if (!gArgs.IsArgSet("-connect") || gArgs.GetArgs("-connect").size() != 1 || gArgs.GetArgs("-connect")[0] != "0")
threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this)));
// Process messages
@@ -2483,7 +2487,7 @@ bool CConnman::DisconnectNode(NodeId id)
{
LOCK(cs_vNodes);
for(CNode* pnode : vNodes) {
- if (id == pnode->id) {
+ if (id == pnode->GetId()) {
pnode->fDisconnect = true;
return true;
}
@@ -2621,10 +2625,10 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
nTimeConnected(GetSystemTimeInSeconds()),
addr(addrIn),
fInbound(fInboundIn),
- id(idIn),
nKeyedNetGroup(nKeyedNetGroupIn),
addrKnown(5000, 0.001),
filterInventoryKnown(50000, 0.000001),
+ id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
nLocalServices(nLocalServicesIn),
nMyStartingHeight(nMyStartingHeightIn),
@@ -2740,7 +2744,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
size_t nMessageSize = msg.data.size();
size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE;
- LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command.c_str()), nMessageSize, pnode->id);
+ LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command.c_str()), nMessageSize, pnode->GetId());
std::vector<unsigned char> serializedHeader;
serializedHeader.reserve(CMessageHeader::HEADER_SIZE);
@@ -2778,7 +2782,7 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
CNode* found = nullptr;
LOCK(cs_vNodes);
for (auto&& pnode : vNodes) {
- if(pnode->id == id) {
+ if(pnode->GetId() == id) {
found = pnode;
break;
}
diff --git a/src/net.h b/src/net.h
index 0fb0ad5039..bea04bc580 100644
--- a/src/net.h
+++ b/src/net.h
@@ -11,10 +11,10 @@
#include "amount.h"
#include "bloom.h"
#include "compat.h"
-#include "fs.h"
#include "hash.h"
#include "limitedmap.h"
#include "netaddress.h"
+#include "policy/feerate.h"
#include "protocol.h"
#include "random.h"
#include "streams.h"
@@ -36,7 +36,6 @@
#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
-class CAddrMan;
class CScheduler;
class CNode;
@@ -92,7 +91,7 @@ static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK;
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
-typedef int NodeId;
+typedef int64_t NodeId;
struct AddedNodeInfo
{
@@ -613,7 +612,6 @@ public:
CCriticalSection cs_filter;
CBloomFilter* pfilter;
std::atomic<int> nRefCount;
- const NodeId id;
const uint64_t nKeyedNetGroup;
std::atomic_bool fPauseRecv;
@@ -684,6 +682,7 @@ public:
private:
CNode(const CNode&);
void operator=(const CNode&);
+ const NodeId id;
const uint64_t nLocalHostNonce;
@@ -701,15 +700,15 @@ private:
public:
NodeId GetId() const {
- return id;
+ return id;
}
uint64_t GetLocalNonce() const {
- return nLocalHostNonce;
+ return nLocalHostNonce;
}
int GetMyStartingHeight() const {
- return nMyStartingHeight;
+ return nMyStartingHeight;
}
int GetRefCount()
@@ -760,7 +759,7 @@ public:
// after addresses were pushed.
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
- vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr;
+ vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
vAddrToSend.push_back(_addr);
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5846c3a770..4d924b5cdb 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -744,21 +744,23 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanI
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
}
-void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
- if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
- return;
-
+void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
LOCK(cs_main);
std::vector<uint256> vOrphanErase;
- // Which orphan pool entries must we evict?
- for (size_t j = 0; j < tx.vin.size(); j++) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = *(*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ const CTransaction& tx = *ptx;
+
+ // Which orphan pool entries must we evict?
+ for (size_t j = 0; j < tx.vin.size(); j++) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
+ if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = *(*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
}
}
@@ -772,10 +774,12 @@ void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIn
}
}
+// All of the following cache a recent block, and are protected by cs_most_recent_block
static CCriticalSection cs_most_recent_block;
static std::shared_ptr<const CBlock> most_recent_block;
static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block;
static uint256 most_recent_block_hash;
+static bool fWitnessesPresentInMostRecentCompactBlock;
void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true);
@@ -796,6 +800,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
most_recent_block_hash = hashBlock;
most_recent_block = pblock;
most_recent_compact_block = pcmpctblock;
+ fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
}
connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
@@ -810,7 +815,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
!PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock",
- hashBlock.ToString(), pnode->id);
+ hashBlock.ToString(), pnode->GetId());
connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
state.pindexBestHeaderSent = pindex;
}
@@ -945,7 +950,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
if (pnode->nVersion >= CADDR_TIME_VERSION) {
- uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
+ uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);
@@ -988,6 +993,15 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
{
bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
+ std::shared_ptr<const CBlock> a_recent_block;
+ std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
+ bool fWitnessesPresentInARecentCompactBlock;
+ {
+ LOCK(cs_most_recent_block);
+ a_recent_block = most_recent_block;
+ a_recent_compact_block = most_recent_compact_block;
+ fWitnessesPresentInARecentCompactBlock = fWitnessesPresentInMostRecentCompactBlock;
+ }
if (mi != mapBlockIndex.end())
{
if (mi->second->nChainTx && !mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
@@ -997,11 +1011,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// before ActivateBestChain but after AcceptBlock).
// In this case, we need to run ActivateBestChain prior to checking the relay
// conditions below.
- std::shared_ptr<const CBlock> a_recent_block;
- {
- LOCK(cs_most_recent_block);
- a_recent_block = most_recent_block;
- }
CValidationState dummy;
ActivateBestChain(dummy, Params(), a_recent_block);
}
@@ -1035,14 +1044,20 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// it's available before trying to send.
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
{
- // Send block from disk
- CBlock block;
- if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
- assert(!"cannot load block from disk");
+ std::shared_ptr<const CBlock> pblock;
+ if (a_recent_block && a_recent_block->GetHash() == (*mi).second->GetBlockHash()) {
+ pblock = a_recent_block;
+ } else {
+ // Send block from disk
+ std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
+ if (!ReadBlockFromDisk(*pblockRead, (*mi).second, consensusParams))
+ assert(!"cannot load block from disk");
+ pblock = pblockRead;
+ }
if (inv.type == MSG_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block));
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
@@ -1051,7 +1066,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
LOCK(pfrom->cs_filter);
if (pfrom->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
}
}
if (sendMerkleBlock) {
@@ -1064,7 +1079,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]));
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1078,10 +1093,15 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
- CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- } else
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block));
+ if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ } else {
+ CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ }
+ } else {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ }
}
// Trigger the peer node to send a getblocks request for the next batch of inventory
@@ -1142,7 +1162,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
}
-uint32_t GetFetchFlags(CNode* pfrom, const CBlockIndex* pprev, const Consensus::Params& chainparams) {
+uint32_t GetFetchFlags(CNode* pfrom) {
uint32_t nFetchFlags = 0;
if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
@@ -1156,7 +1176,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
if (req.indexes[i] >= block.vtx.size()) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id);
+ LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId());
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
@@ -1169,7 +1189,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, const std::atomic<bool>& interruptMsgProc)
{
- LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
+ LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (IsArgSet("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 0)) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
@@ -1248,7 +1268,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (pfrom->nServicesExpected & ~nServices)
{
- LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, nServices, pfrom->nServicesExpected);
+ LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected);
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
pfrom->fDisconnect = true;
@@ -1258,7 +1278,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
- LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
+ LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
pfrom->fDisconnect = true;
@@ -1360,7 +1380,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
+ pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(),
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
@@ -1530,9 +1550,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
- uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
-
- std::vector<CInv> vToFetch;
+ uint32_t nFetchFlags = GetFetchFlags(pfrom);
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
@@ -1542,7 +1560,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
bool fAlreadyHave = AlreadyHave(inv);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->GetId());
if (inv.type == MSG_TX) {
inv.type |= nFetchFlags;
@@ -1557,14 +1575,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// we now only provide a getheaders response here. When we receive the headers, we will
// then ask for the blocks we need.
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
- LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId());
}
}
else
{
pfrom->AddInventoryKnown(inv);
if (fBlocksOnly) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
} else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) {
pfrom->AskFor(inv);
}
@@ -1573,9 +1591,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Track requests for our stuff
GetMainSignals().Inventory(inv.hash);
}
-
- if (!vToFetch.empty())
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vToFetch));
}
@@ -1590,10 +1605,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return error("message getdata size() = %u", vInv.size());
}
- LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
+ LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->GetId());
if (vInv.size() > 0) {
- LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->GetId());
}
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
@@ -1633,7 +1648,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pindex)
pindex = chainActive.Next(pindex);
int nLimit = 500;
- LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
+ LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId());
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -1683,7 +1698,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
- LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
+ LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId());
return true;
}
@@ -1695,7 +1710,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// might maliciously send lots of getblocktxn requests to trigger
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH);
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
inv.hash = req.blockhash;
@@ -1720,7 +1735,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -1745,7 +1760,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
- LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
@@ -1775,7 +1790,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
{
- LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->id);
+ LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
return true;
}
@@ -1808,7 +1823,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->nLastTXTime = GetTime();
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->id,
+ pfrom->GetId(),
tx.GetHash().ToString(),
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
@@ -1883,7 +1898,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
}
if (!fRejectedParents) {
- uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
+ uint32_t nFetchFlags = GetFetchFlags(pfrom);
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv);
@@ -1928,10 +1943,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// case.
int nDoS = 0;
if (!state.IsInvalid(nDoS) || nDoS == 0) {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
RelayTransaction(tx, connman);
} else {
- LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
}
}
}
@@ -1943,7 +1958,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (state.IsInvalid(nDoS))
{
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
- pfrom->id,
+ pfrom->GetId(),
FormatStateMessage(state));
if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
@@ -1980,7 +1995,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
}
- LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
+ LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
return true;
}
}
@@ -2020,7 +2035,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We requested this block for some reason, but our mempool will probably be useless
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
return true;
@@ -2059,12 +2074,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id);
+ LogPrintf("Peer %d sent us invalid compact block\n", pfrom->GetId());
return true;
} else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return true;
}
@@ -2107,7 +2122,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We requested this block, but its far into the future, so our
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return true;
} else {
@@ -2164,7 +2179,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
it->second.first != pfrom->GetId()) {
- LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
+ LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->GetId());
return true;
}
@@ -2173,12 +2188,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
+ LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId());
return true;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
- invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
+ invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
@@ -2261,7 +2276,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
pindexBestHeader->nHeight,
- pfrom->id, nodestate->nUnconnectingHeaders);
+ pfrom->GetId(), nodestate->nUnconnectingHeaders);
// Set hashLastUnknownBlock for this peer, so that if we
// eventually get the headers - even from a different peer -
// we can use this peer to download.
@@ -2299,7 +2314,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
CNodeState *nodestate = State(pfrom->GetId());
if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
}
nodestate->nUnconnectingHeaders = 0;
@@ -2310,7 +2325,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
// from there instead.
- LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
+ LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
}
@@ -2346,11 +2361,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Can't download any more from this peer
break;
}
- uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus());
+ uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->id);
+ pindex->GetBlockHash().ToString(), pfrom->GetId());
}
if (vGetData.size() > 1) {
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
@@ -2373,7 +2388,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
- LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
// Process all blocks from whitelisted peers, even if not requested,
// unless we're still syncing with the network.
@@ -2405,14 +2420,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
if (!pfrom->fInbound) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id);
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
return true;
}
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
if (pfrom->fSentAddr) {
- LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
+ LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->GetId());
return true;
}
pfrom->fSentAddr = true;
@@ -2513,7 +2528,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!(sProblem.empty())) {
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->id,
+ pfrom->GetId(),
sProblem,
pfrom->nPingNonceSent,
nonce,
@@ -2590,7 +2605,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(pfrom->cs_feeFilter);
pfrom->minFeeFilter = newFeeFilter;
}
- LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
+ LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
}
}
@@ -2601,7 +2616,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
else {
// Ignore unknown commands for extensibility
- LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
+ LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId());
}
@@ -2661,100 +2676,100 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return true;
- // Don't bother if send buffer is too full to respond anyway
- if (pfrom->fPauseSend)
- return false;
+ // Don't bother if send buffer is too full to respond anyway
+ if (pfrom->fPauseSend)
+ return false;
- std::list<CNetMessage> msgs;
- {
- LOCK(pfrom->cs_vProcessMsg);
- if (pfrom->vProcessMsg.empty())
- return false;
- // Just take one message
- msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
- pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
- pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize();
- fMoreWork = !pfrom->vProcessMsg.empty();
- }
- CNetMessage& msg(msgs.front());
-
- msg.SetVersion(pfrom->GetRecvVersion());
- // Scan for message start
- if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
- pfrom->fDisconnect = true;
+ std::list<CNetMessage> msgs;
+ {
+ LOCK(pfrom->cs_vProcessMsg);
+ if (pfrom->vProcessMsg.empty())
return false;
- }
+ // Just take one message
+ msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
+ pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
+ pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize();
+ fMoreWork = !pfrom->vProcessMsg.empty();
+ }
+ CNetMessage& msg(msgs.front());
+
+ msg.SetVersion(pfrom->GetRecvVersion());
+ // Scan for message start
+ if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
+ LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return false;
+ }
- // Read header
- CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(chainparams.MessageStart()))
- {
- LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
- return fMoreWork;
- }
- std::string strCommand = hdr.GetCommand();
+ // Read header
+ CMessageHeader& hdr = msg.hdr;
+ if (!hdr.IsValid(chainparams.MessageStart()))
+ {
+ LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->GetId());
+ return fMoreWork;
+ }
+ std::string strCommand = hdr.GetCommand();
- // Message size
- unsigned int nMessageSize = hdr.nMessageSize;
+ // Message size
+ unsigned int nMessageSize = hdr.nMessageSize;
- // Checksum
- CDataStream& vRecv = msg.vRecv;
- const uint256& hash = msg.GetMessageHash();
- if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ // Checksum
+ CDataStream& vRecv = msg.vRecv;
+ const uint256& hash = msg.GetMessageHash();
+ if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ {
+ LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
+ SanitizeString(strCommand), nMessageSize,
+ HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
+ HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
+ return fMoreWork;
+ }
+
+ // Process message
+ bool fRet = false;
+ try
+ {
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
+ if (interruptMsgProc)
+ return false;
+ if (!pfrom->vRecvGetData.empty())
+ fMoreWork = true;
+ }
+ catch (const std::ios_base::failure& e)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
+ if (strstr(e.what(), "end of data"))
{
- LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
- SanitizeString(strCommand), nMessageSize,
- HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
- HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
- return fMoreWork;
+ // Allow exceptions from under-length message on vRecv
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
-
- // Process message
- bool fRet = false;
- try
+ else if (strstr(e.what(), "size too large"))
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
- if (interruptMsgProc)
- return false;
- if (!pfrom->vRecvGetData.empty())
- fMoreWork = true;
+ // Allow exceptions from over-long size
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
- catch (const std::ios_base::failure& e)
+ else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
{
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
- if (strstr(e.what(), "end of data"))
- {
- // Allow exceptions from under-length message on vRecv
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
- // Allow exceptions from over-long size
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
- // Allow exceptions from non-canonical encoding
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
- PrintExceptionContinue(&e, "ProcessMessages()");
- }
+ // Allow exceptions from non-canonical encoding
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
- catch (const std::exception& e) {
+ else
+ {
PrintExceptionContinue(&e, "ProcessMessages()");
- } catch (...) {
- PrintExceptionContinue(NULL, "ProcessMessages()");
}
+ }
+ catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "ProcessMessages()");
+ }
- if (!fRet) {
- LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
- }
+ if (!fRet) {
+ LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->GetId());
+ }
- LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, connman);
+ LOCK(cs_main);
+ SendRejectsAndCheckIfBanned(pfrom, connman);
return fMoreWork;
}
@@ -2879,7 +2894,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
got back an empty response. */
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
- LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
+ LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
}
}
@@ -2909,7 +2924,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
(!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) ||
pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
const CBlockIndex *pBestIndex = NULL; // last header queued for delivery
- ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
+ ProcessBlockAvailability(pto->GetId()); // ensure pindexBestKnownBlock is up-to-date
if (!fRevertToInv) {
bool fFoundStartingHeader = false;
@@ -2964,7 +2979,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// We only send up to 1 block as header-and-ids, as otherwise
// probably means we're doing an initial-ish-sync or they're slow
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
+ vHeaders.front().GetHash().ToString(), pto->GetId());
int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
@@ -2972,7 +2987,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
{
LOCK(cs_most_recent_block);
if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
- if (state.fWantsCmpctWitness)
+ if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
else {
CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
@@ -2994,10 +3009,10 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
LogPrint(BCLog::NET, "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
vHeaders.size(),
vHeaders.front().GetHash().ToString(),
- vHeaders.back().GetHash().ToString(), pto->id);
+ vHeaders.back().GetHash().ToString(), pto->GetId());
} else {
LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
+ vHeaders.front().GetHash().ToString(), pto->GetId());
}
connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
state.pindexBestHeaderSent = pBestIndex;
@@ -3026,7 +3041,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
if (!PeerHasHeader(&state, pindex)) {
pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__,
- pto->id, hashToAnnounce.ToString());
+ pto->GetId(), hashToAnnounce.ToString());
}
}
}
@@ -3173,7 +3188,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
// should only happen during initial block download.
- LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
+ LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -3186,7 +3201,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -3201,11 +3216,11 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
BOOST_FOREACH(const CBlockIndex *pindex, vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
+ uint32_t nFetchFlags = GetFetchFlags(pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
- pindex->nHeight, pto->id);
+ pindex->nHeight, pto->GetId());
}
if (state.nBlocksInFlight == 0 && staller != -1) {
if (State(staller)->nStallingSince == 0) {
@@ -3223,7 +3238,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
const CInv& inv = (*pto->mapAskFor.begin()).second;
if (!AlreadyHave(inv))
{
- LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->id);
+ LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
diff --git a/src/net_processing.h b/src/net_processing.h
index 9e3f1b7156..f460595bc1 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -30,10 +30,10 @@ private:
public:
PeerLogicValidation(CConnman* connmanIn);
- virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
- virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
- virtual void BlockChecked(const CBlock& block, const CValidationState& state);
- virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock);
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
+ void BlockChecked(const CBlock& block, const CValidationState& state) override;
+ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
};
struct CNodeStateStats {
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index ab07270f3f..34a7029862 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -179,12 +179,6 @@ bool CNetAddr::IsLocal() const
return false;
}
-bool CNetAddr::IsMulticast() const
-{
- return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0)
- || (GetByte(15) == 0xFF);
-}
-
bool CNetAddr::IsValid() const
{
// Cleanup 3-byte shifted addresses caused by garbage in size field
diff --git a/src/netaddress.h b/src/netaddress.h
index a85c2b7452..fbc4d1a65f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -65,7 +65,6 @@ class CNetAddr
bool IsLocal() const;
bool IsRoutable() const;
bool IsValid() const;
- bool IsMulticast() const;
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
diff --git a/src/amount.cpp b/src/policy/feerate.cpp
index a5f6bc3cd9..a089c02284 100644
--- a/src/amount.cpp
+++ b/src/policy/feerate.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "amount.h"
+#include "feerate.h"
#include "tinyformat.h"
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
new file mode 100644
index 0000000000..e82268b095
--- /dev/null
+++ b/src/policy/feerate.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_POLICY_FEERATE_H
+#define BITCOIN_POLICY_FEERATE_H
+
+#include "amount.h"
+#include "serialize.h"
+
+#include <string>
+
+extern const std::string CURRENCY_UNIT;
+
+/**
+ * Fee rate in satoshis per kilobyte: CAmount / kB
+ */
+class CFeeRate
+{
+private:
+ CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
+public:
+ /** Fee rate of 0 satoshis per kB */
+ CFeeRate() : nSatoshisPerK(0) { }
+ explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
+ /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/
+ CFeeRate(const CAmount& nFeePaid, size_t nBytes);
+ CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; }
+ /**
+ * Return the fee in satoshis for the given size in bytes.
+ */
+ CAmount GetFee(size_t nBytes) const;
+ /**
+ * Return the fee in satoshis for a size of 1000 bytes
+ */
+ CAmount GetFeePerK() const { return GetFee(1000); }
+ friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
+ friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
+ friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
+ friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
+ friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
+ CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
+ std::string ToString() const;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(nSatoshisPerK);
+ }
+};
+
+#endif // BITCOIN_POLICY_FEERATE_H
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 38e07dc345..bd169f875a 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -7,14 +7,121 @@
#include "policy/policy.h"
#include "amount.h"
+#include "clientversion.h"
#include "primitives/transaction.h"
#include "random.h"
#include "streams.h"
#include "txmempool.h"
#include "util.h"
-void TxConfirmStats::Initialize(std::vector<double>& defaultBuckets,
- unsigned int maxConfirms, double _decay)
+/**
+ * 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
+ * approximate feerate and then track how long it took for those txs to be included in a block
+ *
+ * The tracking of unconfirmed (mempool) transactions is completely independent of the
+ * historical tracking of transactions that have been confirmed in a block.
+ */
+class TxConfirmStats
+{
+private:
+ //Define the buckets we will group transactions into
+ std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
+ std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
+
+ // For each bucket X:
+ // Count the total # of txs in each bucket
+ // Track the historical moving average of this total over blocks
+ std::vector<double> txCtAvg;
+ // and calculate the total for the current block to update the moving average
+ std::vector<int> curBlockTxCt;
+
+ // Count the total # of txs confirmed within Y blocks in each bucket
+ // Track the historical moving average of theses totals over blocks
+ std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
+ // and calculate the totals for the current block to update the moving averages
+ std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
+
+ // Sum the total feerate of all tx's in each bucket
+ // Track the historical moving average of this total over blocks
+ std::vector<double> avg;
+ // and calculate the total for the current block to update the moving average
+ std::vector<double> curBlockVal;
+
+ // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
+ // Combine the total value with the tx counts to calculate the avg feerate per bucket
+
+ double decay;
+
+ // Mempool counts of outstanding transactions
+ // For each bucket X, track the number of transactions in the mempool
+ // that are unconfirmed for each possible confirmation value Y
+ std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
+ // transactions still unconfirmed after MAX_CONFIRMS for each bucket
+ std::vector<int> oldUnconfTxs;
+
+public:
+ /**
+ * Create new TxConfirmStats. This is called by BlockPolicyEstimator's
+ * constructor with default values.
+ * @param defaultBuckets contains the upper limits for the bucket boundaries
+ * @param maxConfirms max number of confirms to track
+ * @param decay how much to decay the historical moving average per block
+ */
+ TxConfirmStats(const std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
+
+ /** Clear the state of the curBlock variables to start counting for the new block */
+ void ClearCurrent(unsigned int nBlockHeight);
+
+ /**
+ * Record a new transaction data point in the current block stats
+ * @param blocksToConfirm the number of blocks it took this transaction to confirm
+ * @param val the feerate of the transaction
+ * @warning blocksToConfirm is 1-based and has to be >= 1
+ */
+ void Record(int blocksToConfirm, double val);
+
+ /** Record a new transaction entering the mempool*/
+ unsigned int NewTx(unsigned int nBlockHeight, double val);
+
+ /** Remove a transaction from mempool tracking stats*/
+ void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
+ unsigned int bucketIndex);
+
+ /** Update our estimates by decaying our historical moving average and updating
+ with the data gathered from the current block */
+ void UpdateMovingAverages();
+
+ /**
+ * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
+ * to make sure we have enough data points) whose transactions still have sufficient likelihood
+ * of being confirmed within the target number of confirmations
+ * @param confTarget target number of confirmations
+ * @param sufficientTxVal required average number of transactions per block in a bucket range
+ * @param minSuccess the success probability we require
+ * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
+ * return the highest feerate such that all lower values fail minSuccess
+ * @param nBlockHeight the current block height
+ */
+ double EstimateMedianVal(int confTarget, double sufficientTxVal,
+ double minSuccess, bool requireGreater, unsigned int nBlockHeight) const;
+
+ /** Return the max number of confirms we're tracking */
+ unsigned int GetMaxConfirms() const { return confAvg.size(); }
+
+ /** Write state of estimation data to a file*/
+ void Write(CAutoFile& fileout) const;
+
+ /**
+ * Read saved state of estimation data from a file and replace all internal data structures and
+ * variables with this state.
+ */
+ void Read(CAutoFile& filein);
+};
+
+
+TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
+ unsigned int maxConfirms, double _decay)
{
decay = _decay;
for (unsigned int i = 0; i < defaultBuckets.size(); i++) {
@@ -77,7 +184,7 @@ void TxConfirmStats::UpdateMovingAverages()
// returns -1 on error conditions
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
double successBreakPoint, bool requireGreater,
- unsigned int nBlockHeight)
+ unsigned int nBlockHeight) const
{
// Counters for a bucket (or range of buckets)
double nConf = 0; // Number of tx's confirmed within the confTarget
@@ -173,7 +280,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
return median;
}
-void TxConfirmStats::Write(CAutoFile& fileout)
+void TxConfirmStats::Write(CAutoFile& fileout) const
{
fileout << decay;
fileout << buckets;
@@ -290,9 +397,10 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
// of no harm to try to remove them again.
bool CBlockPolicyEstimator::removeTx(uint256 hash)
{
+ LOCK(cs_feeEstimator);
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
if (pos != mapMemPoolTxs.end()) {
- feeStats.removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
+ feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
mapMemPoolTxs.erase(hash);
return true;
} else {
@@ -310,11 +418,17 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
vfeelist.push_back(bucketBoundary);
}
vfeelist.push_back(INF_FEERATE);
- feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
+ feeStats = new TxConfirmStats(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
+}
+
+CBlockPolicyEstimator::~CBlockPolicyEstimator()
+{
+ delete feeStats;
}
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
{
+ LOCK(cs_feeEstimator);
unsigned int txHeight = entry.GetHeight();
uint256 hash = entry.GetTx().GetHash();
if (mapMemPoolTxs.count(hash)) {
@@ -343,7 +457,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
mapMemPoolTxs[hash].blockHeight = txHeight;
- mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK());
+ mapMemPoolTxs[hash].bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
}
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
@@ -367,13 +481,14 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
// Feerates are stored and reported as BTC-per-kb:
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
- feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK());
+ feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
return true;
}
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
std::vector<const CTxMemPoolEntry*>& entries)
{
+ LOCK(cs_feeEstimator);
if (nBlockHeight <= nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random
// they don't affect the estimate.
@@ -389,7 +504,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
nBestSeenHeight = nBlockHeight;
// Clear the current block state and update unconfirmed circular buffer
- feeStats.ClearCurrent(nBlockHeight);
+ feeStats->ClearCurrent(nBlockHeight);
unsigned int countedTxs = 0;
// Repopulate the current block states
@@ -399,7 +514,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
}
// Update all exponential averages with the current block state
- feeStats.UpdateMovingAverages();
+ feeStats->UpdateMovingAverages();
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy after updating estimates for %u of %u txs in block, since last block %u of %u tracked, new mempool map size %u\n",
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size());
@@ -408,14 +523,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
untrackedTxs = 0;
}
-CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
+CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
{
+ LOCK(cs_feeEstimator);
// Return failure if trying to analyze a target we're not tracking
// It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget <= 1 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
+ if (confTarget <= 1 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
return CFeeRate(0);
- double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ double median = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
if (median < 0)
return CFeeRate(0);
@@ -423,22 +539,28 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
return CFeeRate(median);
}
-CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
+CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const
{
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget;
- // Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
- return CFeeRate(0);
-
- // It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget == 1)
- confTarget = 2;
double median = -1;
- while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
- median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
- }
+
+ {
+ LOCK(cs_feeEstimator);
+
+ // Return failure if trying to analyze a target we're not tracking
+ if (confTarget <= 0 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
+ return CFeeRate(0);
+
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget == 1)
+ confTarget = 2;
+
+ while (median < 0 && (unsigned int)confTarget <= feeStats->GetMaxConfirms()) {
+ median = feeStats->EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ }
+ } // Must unlock cs_feeEstimator before taking mempool locks
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget - 1;
@@ -454,19 +576,40 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
return CFeeRate(median);
}
-void CBlockPolicyEstimator::Write(CAutoFile& fileout)
+bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
{
- fileout << nBestSeenHeight;
- feeStats.Write(fileout);
+ try {
+ LOCK(cs_feeEstimator);
+ fileout << 139900; // version required to read: 0.13.99 or later
+ fileout << CLIENT_VERSION; // version that wrote the file
+ fileout << nBestSeenHeight;
+ feeStats->Write(fileout);
+ }
+ catch (const std::exception&) {
+ LogPrintf("CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
+ return false;
+ }
+ return true;
}
-void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion)
+bool CBlockPolicyEstimator::Read(CAutoFile& filein)
{
- int nFileBestSeenHeight;
- filein >> nFileBestSeenHeight;
- feeStats.Read(filein);
- nBestSeenHeight = nFileBestSeenHeight;
- // if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
+ try {
+ LOCK(cs_feeEstimator);
+ int nVersionRequired, nVersionThatWrote, nFileBestSeenHeight;
+ filein >> nVersionRequired >> nVersionThatWrote;
+ if (nVersionRequired > CLIENT_VERSION)
+ return error("CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
+ filein >> nFileBestSeenHeight;
+ feeStats->Read(filein);
+ nBestSeenHeight = nFileBestSeenHeight;
+ // if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
+ }
+ catch (const std::exception&) {
+ LogPrintf("CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal)\n");
+ return false;
+ }
+ return true;
}
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
diff --git a/src/policy/fees.h b/src/policy/fees.h
index dd01c90c45..15876574d2 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -6,8 +6,10 @@
#define BITCOIN_POLICYESTIMATOR_H
#include "amount.h"
+#include "feerate.h"
#include "uint256.h"
#include "random.h"
+#include "sync.h"
#include <map>
#include <string>
@@ -17,6 +19,7 @@ class CAutoFile;
class CFeeRate;
class CTxMemPoolEntry;
class CTxMemPool;
+class TxConfirmStats;
/** \class CBlockPolicyEstimator
* The BlockPolicyEstimator is used for estimating the feerate needed
@@ -59,113 +62,6 @@ class CTxMemPool;
* they've been outstanding.
*/
-/**
- * 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
- * approximate feerate and then track how long it took for those txs to be included in a block
- *
- * The tracking of unconfirmed (mempool) transactions is completely independent of the
- * historical tracking of transactions that have been confirmed in a block.
- */
-class TxConfirmStats
-{
-private:
- //Define the buckets we will group transactions into
- std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
- std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
-
- // For each bucket X:
- // Count the total # of txs in each bucket
- // Track the historical moving average of this total over blocks
- std::vector<double> txCtAvg;
- // and calculate the total for the current block to update the moving average
- std::vector<int> curBlockTxCt;
-
- // Count the total # of txs confirmed within Y blocks in each bucket
- // Track the historical moving average of theses totals over blocks
- std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
- // and calculate the totals for the current block to update the moving averages
- std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
-
- // Sum the total feerate of all tx's in each bucket
- // Track the historical moving average of this total over blocks
- std::vector<double> avg;
- // and calculate the total for the current block to update the moving average
- std::vector<double> curBlockVal;
-
- // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
- // Combine the total value with the tx counts to calculate the avg feerate per bucket
-
- double decay;
-
- // Mempool counts of outstanding transactions
- // For each bucket X, track the number of transactions in the mempool
- // that are unconfirmed for each possible confirmation value Y
- std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
- // transactions still unconfirmed after MAX_CONFIRMS for each bucket
- std::vector<int> oldUnconfTxs;
-
-public:
- /**
- * Initialize the data structures. This is called by BlockPolicyEstimator's
- * constructor with default values.
- * @param defaultBuckets contains the upper limits for the bucket boundaries
- * @param maxConfirms max number of confirms to track
- * @param decay how much to decay the historical moving average per block
- */
- void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
-
- /** Clear the state of the curBlock variables to start counting for the new block */
- void ClearCurrent(unsigned int nBlockHeight);
-
- /**
- * Record a new transaction data point in the current block stats
- * @param blocksToConfirm the number of blocks it took this transaction to confirm
- * @param val the feerate of the transaction
- * @warning blocksToConfirm is 1-based and has to be >= 1
- */
- void Record(int blocksToConfirm, double val);
-
- /** Record a new transaction entering the mempool*/
- unsigned int NewTx(unsigned int nBlockHeight, double val);
-
- /** Remove a transaction from mempool tracking stats*/
- void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
- unsigned int bucketIndex);
-
- /** Update our estimates by decaying our historical moving average and updating
- with the data gathered from the current block */
- void UpdateMovingAverages();
-
- /**
- * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
- * to make sure we have enough data points) whose transactions still have sufficient likelihood
- * of being confirmed within the target number of confirmations
- * @param confTarget target number of confirmations
- * @param sufficientTxVal required average number of transactions per block in a bucket range
- * @param minSuccess the success probability we require
- * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
- * return the highest feerate such that all lower values fail minSuccess
- * @param nBlockHeight the current block height
- */
- double EstimateMedianVal(int confTarget, double sufficientTxVal,
- double minSuccess, bool requireGreater, unsigned int nBlockHeight);
-
- /** Return the max number of confirms we're tracking */
- unsigned int GetMaxConfirms() { return confAvg.size(); }
-
- /** Write state of estimation data to a file*/
- void Write(CAutoFile& fileout);
-
- /**
- * Read saved state of estimation data from a file and replace all internal data structures and
- * variables with this state.
- */
- void Read(CAutoFile& filein);
-};
-
-
-
/** Track confirm delays up to 25 blocks, can't estimate beyond that */
static const unsigned int MAX_BLOCK_CONFIRMS = 25;
@@ -204,14 +100,12 @@ class CBlockPolicyEstimator
public:
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
CBlockPolicyEstimator();
+ ~CBlockPolicyEstimator();
/** Process all the transactions that have been included in a block */
void processBlock(unsigned int nBlockHeight,
std::vector<const CTxMemPoolEntry*>& entries);
- /** Process a transaction confirmed in a block*/
- bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
-
/** Process a transaction accepted to the mempool*/
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate);
@@ -219,19 +113,19 @@ public:
bool removeTx(uint256 hash);
/** Return a feerate estimate */
- CFeeRate estimateFee(int confTarget);
+ CFeeRate estimateFee(int confTarget) const;
/** Estimate feerate needed to get be included in a block within
* confTarget blocks. If no answer can be given at confTarget, return an
* estimate at the lowest target where one can be given.
*/
- CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
+ CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const;
/** Write estimation data to a file */
- void Write(CAutoFile& fileout);
+ bool Write(CAutoFile& fileout) const;
/** Read estimation data from a file */
- void Read(CAutoFile& filein, int nFileVersion);
+ bool Read(CAutoFile& filein);
private:
CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main
@@ -247,10 +141,16 @@ private:
std::map<uint256, TxStatsInfo> mapMemPoolTxs;
/** Classes to track historical data on transaction confirmations */
- TxConfirmStats feeStats;
+ TxConfirmStats* feeStats;
unsigned int trackedTxs;
unsigned int untrackedTxs;
+
+ mutable CCriticalSection cs_feeEstimator;
+
+ /** Process a transaction confirmed in a block*/
+ bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
+
};
class FeeFilterRounder
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index ec398f6627..2b19a6714b 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -8,12 +8,50 @@
#include "policy/policy.h"
#include "validation.h"
+#include "coins.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
#include <boost/foreach.hpp>
+CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee)
+{
+ // "Dust" is defined in terms of dustRelayFee,
+ // which has units satoshis-per-kilobyte.
+ // If you'd pay more than 1/3 in fees
+ // to spend something, then we consider it dust.
+ // A typical spendable non-segwit txout is 34 bytes big, and will
+ // need a CTxIn of at least 148 bytes to spend:
+ // so dust is a spendable txout less than
+ // 546*dustRelayFee/1000 (in satoshis).
+ // A typical spendable segwit txout is 31 bytes big, and will
+ // need a CTxIn of at least 67 bytes to spend:
+ // so dust is a spendable txout less than
+ // 294*dustRelayFee/1000 (in satoshis).
+ if (txout.scriptPubKey.IsUnspendable())
+ return 0;
+
+ size_t nSize = GetSerializeSize(txout, SER_DISK, 0);
+ int witnessversion = 0;
+ std::vector<unsigned char> witnessprogram;
+
+ if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
+ // sum the sizes of the parts of a transaction input
+ // with 75% segwit discount applied to the script size.
+ nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
+ } else {
+ nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
+ }
+
+ return 3 * dustRelayFee.GetFee(nSize);
+}
+
+bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee)
+{
+ return (txout.nValue < GetDustThreshold(txout, dustRelayFee));
+}
+
/**
* Check transaction inputs to mitigate two
* potential denial-of-service attacks:
@@ -105,7 +143,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
- } else if (txout.IsDust(dustRelayFee)) {
+ } else if (IsDust(txout, ::dustRelayFee)) {
reason = "dust";
return false;
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 6df541bc0f..2c2ea9d5b8 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -7,12 +7,14 @@
#define BITCOIN_POLICY_POLICY_H
#include "consensus/consensus.h"
+#include "feerate.h"
#include "script/interpreter.h"
#include "script/standard.h"
#include <string>
class CCoinsViewCache;
+class CTxOut;
/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
@@ -72,6 +74,10 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
LOCKTIME_MEDIAN_TIME_PAST;
+CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
+
+bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
+
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false);
/**
* Check for standard transaction types
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index d413e8b087..00ac0b92b5 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -22,8 +22,8 @@ public:
uint256 hash;
uint32_t n;
- COutPoint() { SetNull(); }
- COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
+ COutPoint(): n((uint32_t) -1) { }
+ COutPoint(const uint256& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
ADD_SERIALIZE_METHODS;
@@ -161,43 +161,6 @@ public:
return (nValue == -1);
}
- CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
- {
- // "Dust" is defined in terms of CTransaction::minRelayTxFee,
- // which has units satoshis-per-kilobyte.
- // If you'd pay more than 1/3 in fees
- // to spend something, then we consider it dust.
- // A typical spendable non-segwit txout is 34 bytes big, and will
- // need a CTxIn of at least 148 bytes to spend:
- // so dust is a spendable txout less than
- // 546*minRelayTxFee/1000 (in satoshis).
- // A typical spendable segwit txout is 31 bytes big, and will
- // need a CTxIn of at least 67 bytes to spend:
- // so dust is a spendable txout less than
- // 294*minRelayTxFee/1000 (in satoshis).
- if (scriptPubKey.IsUnspendable())
- return 0;
-
- size_t nSize = GetSerializeSize(*this, SER_DISK, 0);
- int witnessversion = 0;
- std::vector<unsigned char> witnessprogram;
-
- if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
- // sum the sizes of the parts of a transaction input
- // with 75% segwit discount applied to the script size.
- nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
- } else {
- nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
- }
-
- return 3 * minRelayTxFee.GetFee(nSize);
- }
-
- bool IsDust(const CFeeRate &minRelayTxFee) const
- {
- return (nValue < GetDustThreshold(minRelayTxFee));
- }
-
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 4b34e73eb7..f8a99506c1 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -181,7 +181,5 @@ void BanTableModel::sort(int column, Qt::SortOrder order)
bool BanTableModel::shouldShow()
{
- if (priv->size() > 0)
- return true;
- return false;
+ return priv->size() > 0;
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index be79a67990..5c26baef9e 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -478,6 +478,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
+ modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false);
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
@@ -505,8 +506,6 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
-
- modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime()));
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index bb10e49422..de00eacdb9 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -9,6 +9,7 @@
#include "guiutil.h"
#include "peertablemodel.h"
+#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
@@ -36,6 +37,8 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
banTableModel(0),
pollTimer(0)
{
+ cachedBestHeaderHeight = -1;
+ cachedBestHeaderTime = -1;
peerTableModel = new PeerTableModel(this);
banTableModel = new BanTableModel(this);
pollTimer = new QTimer(this);
@@ -74,18 +77,28 @@ int ClientModel::getNumBlocks() const
int ClientModel::getHeaderTipHeight() const
{
- LOCK(cs_main);
- if (!pindexBestHeader)
- return 0;
- return pindexBestHeader->nHeight;
+ if (cachedBestHeaderHeight == -1) {
+ // make sure we initially populate the cache via a cs_main lock
+ // otherwise we need to wait for a tip update
+ LOCK(cs_main);
+ if (pindexBestHeader) {
+ cachedBestHeaderHeight = pindexBestHeader->nHeight;
+ cachedBestHeaderTime = pindexBestHeader->GetBlockTime();
+ }
+ }
+ return cachedBestHeaderHeight;
}
int64_t ClientModel::getHeaderTipTime() const
{
- LOCK(cs_main);
- if (!pindexBestHeader)
- return 0;
- return pindexBestHeader->GetBlockTime();
+ if (cachedBestHeaderTime == -1) {
+ LOCK(cs_main);
+ if (pindexBestHeader) {
+ cachedBestHeaderHeight = pindexBestHeader->nHeight;
+ cachedBestHeaderTime = pindexBestHeader->GetBlockTime();
+ }
+ }
+ return cachedBestHeaderTime;
}
quint64 ClientModel::getTotalBytesRecv() const
@@ -283,6 +296,11 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB
int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
+ if (fHeader) {
+ // cache best headers time and height to reduce future cs_main locks
+ clientmodel->cachedBestHeaderHeight = pIndex->nHeight;
+ clientmodel->cachedBestHeaderTime = pIndex->GetBlockTime();
+ }
// if we are in-sync, update the UI regardless of last update time
if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass a async signal to the UI thread
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 2c10e633b8..4c92e2144e 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -8,6 +8,8 @@
#include <QObject>
#include <QDateTime>
+#include <atomic>
+
class AddressTableModel;
class BanTableModel;
class OptionsModel;
@@ -81,6 +83,10 @@ public:
QString formatClientStartupTime() const;
QString dataDir() const;
+ // caches for the best header
+ mutable std::atomic<int> cachedBestHeaderHeight;
+ mutable std::atomic<int64_t> cachedBestHeaderTime;
+
private:
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 1d19c65753..2a331d4fae 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -15,6 +15,7 @@
#include "wallet/coincontrol.h"
#include "init.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "validation.h" // For mempool
#include "wallet/wallet.h"
@@ -433,8 +434,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
- if (txout.IsDust(dustRelayFee))
- fDust = true;
+ fDust |= IsDust(txout, ::dustRelayFee);
}
}
@@ -512,7 +512,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+ nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, ::mempool, ::feeEstimator);
if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee)
nPayFee = coinControl->nMinimumTotalFee;
@@ -526,10 +526,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (nChange > 0 && nChange < MIN_CHANGE)
{
CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
- if (txout.IsDust(dustRelayFee))
+ if (IsDust(txout, ::dustRelayFee))
{
if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust
- nChange = txout.GetDustThreshold(dustRelayFee);
+ nChange = GetDustThreshold(txout, ::dustRelayFee);
else
{
nPayFee += nChange;
@@ -592,7 +592,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), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
+ dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(nTxConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000;
}
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index cc183908d4..52256ca5c4 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -1235,7 +1235,7 @@
<bool>false</bool>
</property>
<property name="default">
- <bool>true</bool>
+ <bool>false</bool>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 3f3f9b9ccb..bffa81137b 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -251,7 +251,7 @@ bool isDust(const QString& address, const CAmount& amount)
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
- return txOut.IsDust(dustRelayFee);
+ return IsDust(txOut, ::dustRelayFee);
}
QString HtmlEscape(const QString& str, bool fMultiLine)
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index dd75f12076..c31a7a478d 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -219,14 +219,16 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{
CBitcoinAddress address(r.address.toStdString());
+ auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
- if (address.IsValid(Params(CBaseChainParams::MAIN)))
+ if (address.IsValid(*tempChainParams))
{
SelectParams(CBaseChainParams::MAIN);
}
- else if (address.IsValid(Params(CBaseChainParams::TESTNET)))
- {
- SelectParams(CBaseChainParams::TESTNET);
+ else {
+ tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
+ if (address.IsValid(*tempChainParams))
+ SelectParams(CBaseChainParams::TESTNET);
}
}
}
@@ -580,7 +582,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
// Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first);
- if (txOut.IsDust(dustRelayFee)) {
+ if (IsDust(txOut, ::dustRelayFee)) {
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
CClientUIInterface::MSG_ERROR);
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 974a71ddca..fff072fd4c 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -166,7 +166,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
switch(index.column())
{
case NetNodeId:
- return rec->nodeStats.nodeid;
+ return (qint64)rec->nodeStats.nodeid;
case Address:
return QString::fromStdString(rec->nodeStats.addrName);
case Subversion:
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 5167232d6a..7f2f83d9f7 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -829,7 +829,7 @@ void RPCConsole::on_lineEdit_returnPressed()
cmdBeforeBrowsing = QString();
- message(CMD_REQUEST, cmd);
+ message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
Q_EMIT cmdRequest(cmd);
cmd = QString::fromStdString(strFilteredCmd);
@@ -1118,7 +1118,7 @@ void RPCConsole::disconnectSelectedNode()
for(int i = 0; i < nodes.count(); i++)
{
// Get currently selected peer address
- NodeId id = nodes.at(i).data().toInt();
+ NodeId id = nodes.at(i).data().toLongLong();
// Find the node, disconnect it and clear the selected node
if(g_connman->DisconnectNode(id))
clearSelectedNode();
@@ -1135,7 +1135,7 @@ void RPCConsole::banSelectedNode(int bantime)
for(int i = 0; i < nodes.count(); i++)
{
// Get currently selected peer address
- NodeId id = nodes.at(i).data().toInt();
+ NodeId id = nodes.at(i).data().toLongLong();
// Get currently selected peer address
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ed7eab03f3..098cda6d32 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,6 +21,7 @@
#include "validation.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
+#include "policy/fees.h"
#include "wallet/wallet.h"
#include <QFontMetrics>
@@ -114,7 +115,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
- ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
@@ -175,6 +175,9 @@ void SendCoinsDialog::setModel(WalletModel *_model)
updateSmartFeeLabel();
updateGlobalFeeVariables();
+ // set default rbf checkbox state
+ ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
+
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
@@ -660,7 +663,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
- CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks, ::mempool);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index f794b6b382..a0dce3d997 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -86,7 +86,8 @@ void WalletTests::walletTests()
TestChain100Setup test;
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
bitdb.MakeMock();
- CWallet wallet("wallet_test.dat");
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
+ CWallet wallet(std::move(dbw));
bool firstRun;
wallet.LoadWallet(firstRun);
{
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index a9d9b6887e..4bb260aa58 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -18,14 +18,8 @@
*/
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
- if (wtx.IsCoinBase())
- {
- // Ensures we show generated coins / mined transactions at depth 1
- if (!wtx.IsInMainChain())
- {
- return false;
- }
- }
+ // There are currently no cases where we hide transactions, but
+ // we may want to use this in the future for things like RBF.
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index ebcac53c25..a2a9271904 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -13,6 +13,7 @@
#include "transactiontablemodel.h"
#include "base58.h"
+#include "chain.h"
#include "keystore.h"
#include "validation.h"
#include "net.h" // for g_connman
diff --git a/src/random.cpp b/src/random.cpp
index 6bcd0a70ba..3b9df3edaa 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -16,6 +16,8 @@
#include <stdlib.h>
#include <limits>
+#include <chrono>
+#include <thread>
#ifndef WIN32
#include <sys/time.h>
@@ -32,6 +34,8 @@
#include <sys/sysctl.h>
#endif
+#include <mutex>
+
#include <openssl/err.h>
#include <openssl/rand.h>
@@ -43,15 +47,22 @@ static void RandFailure()
static inline int64_t GetPerformanceCounter()
{
- int64_t nCounter = 0;
-#ifdef WIN32
- QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
+ // Read the hardware time stamp counter when available.
+ // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information.
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+ return __rdtsc();
+#elif !defined(_MSC_VER) && defined(__i386__)
+ uint64_t r = 0;
+ __asm__ volatile ("rdtsc" : "=A"(r)); // Constrain the r variable to the eax:edx pair.
+ return r;
+#elif !defined(_MSC_VER) && (defined(__x86_64__) || defined(__amd64__))
+ uint64_t r1 = 0, r2 = 0;
+ __asm__ volatile ("rdtsc" : "=a"(r1), "=d"(r2)); // Constrain r1 to rax and r2 to rdx.
+ return (r2 << 32) | r1;
#else
- timeval t;
- gettimeofday(&t, NULL);
- nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec);
+ // Fall back to using C++11 clock (usually microsecond or nanosecond precision)
+ return std::chrono::high_resolution_clock::now().time_since_epoch().count();
#endif
- return nCounter;
}
void RandAddSeed()
@@ -192,6 +203,10 @@ void GetRandBytes(unsigned char* buf, int num)
}
}
+static std::mutex cs_rng_state;
+static unsigned char rng_state[32] = {0};
+static uint64_t rng_counter = 0;
+
void GetStrongRandBytes(unsigned char* out, int num)
{
assert(num <= 32);
@@ -207,8 +222,17 @@ void GetStrongRandBytes(unsigned char* out, int num)
GetOSRand(buf);
hasher.Write(buf, 32);
+ // Combine with and update state
+ {
+ std::unique_lock<std::mutex> lock(cs_rng_state);
+ hasher.Write(rng_state, sizeof(rng_state));
+ hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
+ ++rng_counter;
+ hasher.Finalize(buf);
+ memcpy(rng_state, buf + 32, 32);
+ }
+
// Produce output
- hasher.Finalize(buf);
memcpy(out, buf, num);
memory_cleanse(buf, 64);
}
@@ -240,26 +264,22 @@ uint256 GetRandHash()
return hash;
}
-FastRandomContext::FastRandomContext(bool fDeterministic)
+void FastRandomContext::RandomSeed()
{
- // The seed values have some unlikely fixed points which we avoid.
- if (fDeterministic) {
- Rz = Rw = 11;
- } else {
- uint32_t tmp;
- do {
- GetRandBytes((unsigned char*)&tmp, 4);
- } while (tmp == 0 || tmp == 0x9068ffffU);
- Rz = tmp;
- do {
- GetRandBytes((unsigned char*)&tmp, 4);
- } while (tmp == 0 || tmp == 0x464fffffU);
- Rw = tmp;
- }
+ uint256 seed = GetRandHash();
+ rng.SetKey(seed.begin(), 32);
+ requires_seed = false;
+}
+
+FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0)
+{
+ rng.SetKey(seed.begin(), 32);
}
bool Random_SanityCheck()
{
+ uint64_t start = GetPerformanceCounter();
+
/* This does not measure the quality of randomness, but it does test that
* OSRandom() overwrites all 32 bytes of the output given a maximum
* number of tries.
@@ -286,5 +306,25 @@ bool Random_SanityCheck()
tries += 1;
} while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES);
- return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */
+ if (num_overwritten != NUM_OS_RANDOM_BYTES) return false; /* If this failed, bailed out after too many tries */
+
+ // Check that GetPerformanceCounter increases at least during a GetOSRand() call + 1ms sleep.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ uint64_t stop = GetPerformanceCounter();
+ if (stop == start) return false;
+
+ // We called GetPerformanceCounter. Use it as entropy.
+ RAND_add((const unsigned char*)&start, sizeof(start), 1);
+ RAND_add((const unsigned char*)&stop, sizeof(stop), 1);
+
+ return true;
+}
+
+FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
+{
+ if (!fDeterministic) {
+ return;
+ }
+ uint256 seed;
+ rng.SetKey(seed.begin(), 32);
}
diff --git a/src/random.h b/src/random.h
index 0464bdce14..9551e1c461 100644
--- a/src/random.h
+++ b/src/random.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_RANDOM_H
#define BITCOIN_RANDOM_H
+#include "crypto/chacha20.h"
+#include "crypto/common.h"
#include "uint256.h"
#include <stdint.h>
@@ -33,17 +35,79 @@ void GetStrongRandBytes(unsigned char* buf, int num);
* This class is not thread-safe.
*/
class FastRandomContext {
+private:
+ bool requires_seed;
+ ChaCha20 rng;
+
+ unsigned char bytebuf[64];
+ int bytebuf_size;
+
+ uint64_t bitbuf;
+ int bitbuf_size;
+
+ void RandomSeed();
+
+ void FillByteBuffer()
+ {
+ if (requires_seed) {
+ RandomSeed();
+ }
+ rng.Output(bytebuf, sizeof(bytebuf));
+ bytebuf_size = sizeof(bytebuf);
+ }
+
+ void FillBitBuffer()
+ {
+ bitbuf = rand64();
+ bitbuf_size = 64;
+ }
+
public:
- explicit FastRandomContext(bool fDeterministic=false);
+ explicit FastRandomContext(bool fDeterministic = false);
+
+ /** Initialize with explicit seed (only for testing) */
+ explicit FastRandomContext(const uint256& seed);
+
+ /** Generate a random 64-bit integer. */
+ uint64_t rand64()
+ {
+ if (bytebuf_size < 8) FillByteBuffer();
+ uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size);
+ bytebuf_size -= 8;
+ return ret;
+ }
- uint32_t rand32() {
- Rz = 36969 * (Rz & 65535) + (Rz >> 16);
- Rw = 18000 * (Rw & 65535) + (Rw >> 16);
- return (Rw << 16) + Rz;
+ /** Generate a random (bits)-bit integer. */
+ uint64_t randbits(int bits) {
+ if (bits == 0) {
+ return 0;
+ } else if (bits > 32) {
+ return rand64() >> (64 - bits);
+ } else {
+ if (bitbuf_size < bits) FillBitBuffer();
+ uint64_t ret = bitbuf & (~(uint64_t)0 >> (64 - bits));
+ bitbuf >>= bits;
+ bitbuf_size -= bits;
+ return ret;
+ }
}
- uint32_t Rz;
- uint32_t Rw;
+ /** Generate a random integer in the range [0..range). */
+ uint64_t randrange(uint64_t range)
+ {
+ --range;
+ int bits = CountBits(range);
+ while (true) {
+ uint64_t ret = randbits(bits);
+ if (ret <= range) return ret;
+ }
+ }
+
+ /** Generate a random 32-bit integer. */
+ uint32_t rand32() { return randbits(32); }
+
+ /** Generate a random boolean. */
+ bool randbool() { return randbits(1); }
};
/* Number of random bytes returned by GetOSRand.
diff --git a/src/rest.cpp b/src/rest.cpp
index 9dcaf269d6..7537ed4502 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -5,6 +5,7 @@
#include "chain.h"
#include "chainparams.h"
+#include "core_io.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "validation.h"
@@ -56,10 +57,6 @@ struct CCoin {
}
};
-/* Defined in rawtransaction.cpp */
-void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
-
static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
{
req->WriteHeader("Content-Type", "text/plain");
@@ -383,7 +380,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
case RF_JSON: {
UniValue objTx(UniValue::VOBJ);
- TxToJSON(*tx, hashBlock, objTx);
+ TxToUniv(*tx, hashBlock, objTx);
std::string strJSON = objTx.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -577,7 +574,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// include the script in a json output
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true);
+ ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
utxo.push_back(Pair("scriptPubKey", o));
utxos.push_back(utxo);
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 421a52d51a..9d72a23e6d 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -12,6 +12,8 @@
#include "coins.h"
#include "consensus/validation.h"
#include "validation.h"
+#include "core_io.h"
+#include "policy/feerate.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
@@ -42,7 +44,6 @@ static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
double GetDifficulty(const CBlockIndex* blockindex)
{
@@ -123,7 +124,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
if(txDetails)
{
UniValue objTx(UniValue::VOBJ);
- TxToJSON(*tx, uint256(), objTx);
+ TxToUniv(*tx, uint256(), objTx);
txs.push_back(objTx);
}
else
@@ -412,6 +413,7 @@ UniValue getrawmempool(const JSONRPCRequest& request)
throw std::runtime_error(
"getrawmempool ( verbose )\n"
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
+ "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n"
"\nArguments:\n"
"1. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
"\nResult: (for verbose = false):\n"
@@ -988,7 +990,7 @@ UniValue gettxout(const JSONRPCRequest& request)
ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1));
ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue)));
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true);
+ ScriptPubKeyToUniv(coins.vout[n].scriptPubKey, o, true);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("version", coins.nVersion));
ret.push_back(Pair("coinbase", coins.fCoinBase));
@@ -1428,10 +1430,76 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
return NullUniValue;
}
+UniValue getchaintxstats(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 2)
+ throw std::runtime_error(
+ "getchaintxstats ( nblocks blockhash )\n"
+ "\nCompute statistics about the total number and rate of transactions in the chain.\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n"
+ "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n"
+ "\nResult:\n"
+ "{\n"
+ " \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n"
+ " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
+ " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getchaintxstats", "")
+ + HelpExampleRpc("getchaintxstats", "2016")
+ );
+
+ const CBlockIndex* pindex;
+ int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
+
+ if (request.params.size() > 0 && !request.params[0].isNull()) {
+ blockcount = request.params[0].get_int();
+ }
+
+ bool havehash = request.params.size() > 1 && !request.params[1].isNull();
+ uint256 hash;
+ if (havehash) {
+ hash = uint256S(request.params[1].get_str());
+ }
+
+ {
+ LOCK(cs_main);
+ if (havehash) {
+ auto it = mapBlockIndex.find(hash);
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ pindex = it->second;
+ if (!chainActive.Contains(pindex)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
+ }
+ } else {
+ pindex = chainActive.Tip();
+ }
+ }
+
+ if (blockcount < 1 || blockcount >= pindex->nHeight) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height");
+ }
+
+ const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
+ int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast();
+ int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
+
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("time", (int64_t)pindex->nTime));
+ ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx));
+ ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff));
+
+ return ret;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) okSafe argNames
// --------------------- ------------------------ ----------------------- ------ ----------
{ "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} },
+ { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} },
{ "blockchain", "getbestblockhash", &getbestblockhash, true, {} },
{ "blockchain", "getblockcount", &getblockcount, true, {} },
{ "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 9a43649d23..dc916a1fdb 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -79,6 +79,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listunspent", 2, "addresses" },
{ "getblock", 1, "verbosity" },
{ "getblockheader", 1, "verbose" },
+ { "getchaintxstats", 0, "nblocks" },
{ "gettransaction", 1, "include_watchonly" },
{ "getrawtransaction", 1, "verbose" },
{ "createrawtransaction", 0, "inputs" },
@@ -113,6 +114,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
{ "bumpfee", 1, "options" },
+ { "logging", 0, "include" },
+ { "logging", 1, "exclude" },
+ { "disconnectnode", 1, "nodeid" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 7e5f0d608e..4ce52a6c7f 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -15,6 +15,7 @@
#include "validation.h"
#include "miner.h"
#include "net.h"
+#include "policy/fees.h"
#include "pow.h"
#include "rpc/blockchain.h"
#include "rpc/server.h"
@@ -27,7 +28,6 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
-#include <boost/shared_ptr.hpp>
#include <univalue.h>
@@ -95,7 +95,7 @@ UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
static const int nInnerLoopCount = 0x10000;
int nHeightStart = 0;
@@ -167,7 +167,7 @@ UniValue generate(const JSONRPCRequest& request)
nMaxTries = request.params[1].get_int();
}
- boost::shared_ptr<CReserveScript> coinbaseScript;
+ std::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
@@ -208,7 +208,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
- boost::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript());
+ std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
coinbaseScript->reserveScript = GetScriptForDestination(address.Get());
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
@@ -710,7 +710,7 @@ public:
submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}
protected:
- virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
+ void BlockChecked(const CBlock& block, const CValidationState& stateIn) override {
if (block.GetHash() != hash)
return;
found = true;
@@ -819,7 +819,7 @@ UniValue estimatefee(const JSONRPCRequest& request)
if (nBlocks < 1)
nBlocks = 1;
- CFeeRate feeRate = mempool.estimateFee(nBlocks);
+ CFeeRate feeRate = ::feeEstimator.estimateFee(nBlocks);
if (feeRate == CFeeRate(0))
return -1.0;
@@ -857,7 +857,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
int answerFound;
- CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound);
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &answerFound, ::mempool);
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
result.push_back(Pair("blocks", answerFound));
return result;
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 24c5eeffe9..1f973a0c18 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -4,9 +4,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
+#include "chain.h"
#include "clientversion.h"
#include "init.h"
#include "validation.h"
+#include "httpserver.h"
#include "net.h"
#include "netbase.h"
#include "rpc/blockchain.h"
@@ -555,6 +557,73 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
+uint32_t getCategoryMask(UniValue cats) {
+ cats = cats.get_array();
+ uint32_t mask = 0;
+ for (unsigned int i = 0; i < cats.size(); ++i) {
+ uint32_t flag = 0;
+ std::string cat = cats[i].get_str();
+ if (!GetLogCategory(&flag, &cat)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+ }
+ mask |= flag;
+ }
+ return mask;
+}
+
+UniValue logging(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 2) {
+ throw std::runtime_error(
+ "logging [include,...] <exclude>\n"
+ "Gets and sets the logging configuration.\n"
+ "When called without an argument, returns the list of categories that are currently being debug logged.\n"
+ "When called with arguments, adds or removes categories from debug logging.\n"
+ "The valid logging categories are: " + ListLogCategories() + "\n"
+ "libevent logging is configured on startup and cannot be modified by this RPC during runtime."
+ "Arguments:\n"
+ "1. \"include\" (array of strings) add debug logging for these categories.\n"
+ "2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
+ "\nResult: <categories> (string): a list of the logging categories that are active.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
+ );
+ }
+
+ uint32_t originalLogCategories = logCategories;
+ if (request.params.size() > 0 && request.params[0].isArray()) {
+ logCategories |= getCategoryMask(request.params[0]);
+ }
+
+ if (request.params.size() > 1 && request.params[1].isArray()) {
+ logCategories &= ~getCategoryMask(request.params[1]);
+ }
+
+ // Update libevent logging if BCLog::LIBEVENT has changed.
+ // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
+ // in which case we should clear the BCLog::LIBEVENT flag.
+ // Throw an error if the user has explicitly asked to change only the libevent
+ // flag and it failed.
+ uint32_t changedLogCategories = originalLogCategories ^ logCategories;
+ if (changedLogCategories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
+ if (changedLogCategories == BCLog::LIBEVENT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
+ }
+ }
+ }
+
+ UniValue result(UniValue::VOBJ);
+ std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
+ for (const auto& logCatActive : vLogCatActive) {
+ result.pushKV(logCatActive.category, logCatActive.active);
+ }
+
+ return result;
+}
+
UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
@@ -581,7 +650,8 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, true, {"timestamp"}},
{ "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "logging", &logging, true, {"include", "exclude"}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 44c6e6d308..cde5ae723b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -234,23 +234,43 @@ UniValue addnode(const JSONRPCRequest& request)
UniValue disconnectnode(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
+ if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
- "disconnectnode \"node\" \n"
- "\nImmediately disconnects from the specified node.\n"
+ "disconnectnode \"[address]\" [nodeid]\n"
+ "\nImmediately disconnects from the specified peer node.\n"
+ "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
+ "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
"\nArguments:\n"
- "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
+ "1. \"address\" (string, optional) The IP address/port of the node\n"
+ "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ + HelpExampleCli("disconnectnode", "\"\" 1")
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
+ + HelpExampleRpc("disconnectnode", "\"\", 1")
);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- bool ret = g_connman->DisconnectNode(request.params[0].get_str());
- if (!ret)
+ bool success;
+ const UniValue &address_arg = request.params[0];
+ const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1];
+
+ if (!address_arg.isNull() && id_arg.isNull()) {
+ /* handle disconnect-by-address */
+ success = g_connman->DisconnectNode(address_arg.get_str());
+ } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
+ /* handle disconnect-by-id */
+ NodeId nodeid = (NodeId) id_arg.get_int64();
+ success = g_connman->DisconnectNode(nodeid);
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
+ }
+
+ if (!success) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
+ }
return NullUniValue;
}
@@ -607,7 +627,7 @@ static const CRPCCommand commands[] =
{ "network", "ping", &ping, true, {} },
{ "network", "getpeerinfo", &getpeerinfo, true, {} },
{ "network", "addnode", &addnode, true, {"node","command"} },
- { "network", "disconnectnode", &disconnectnode, true, {"node"} },
+ { "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} },
{ "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} },
{ "network", "getnettotals", &getnettotals, true, {} },
{ "network", "getnetworkinfo", &getnetworkinfo, true, {} },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 717e9d75f3..3947fb3f7d 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -34,77 +34,15 @@
#include <univalue.h>
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
-{
- txnouttype type;
- std::vector<CTxDestination> addresses;
- int nRequired;
-
- out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
- if (fIncludeHex)
- out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
-
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
- out.push_back(Pair("type", GetTxnOutputType(type)));
- return;
- }
-
- out.push_back(Pair("reqSigs", nRequired));
- out.push_back(Pair("type", GetTxnOutputType(type)));
-
- UniValue a(UniValue::VARR);
- BOOST_FOREACH(const CTxDestination& addr, addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
- out.push_back(Pair("addresses", a));
-}
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
- entry.push_back(Pair("txid", tx.GetHash().GetHex()));
- entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex()));
- entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
- entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx)));
- entry.push_back(Pair("version", tx.nVersion));
- entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
-
- UniValue vin(UniValue::VARR);
- for (unsigned int i = 0; i < tx.vin.size(); i++) {
- const CTxIn& txin = tx.vin[i];
- UniValue in(UniValue::VOBJ);
- if (tx.IsCoinBase())
- in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- else {
- in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
- in.push_back(Pair("vout", (int64_t)txin.prevout.n));
- UniValue o(UniValue::VOBJ);
- o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
- o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- in.push_back(Pair("scriptSig", o));
- }
- if (tx.HasWitness()) {
- UniValue txinwitness(UniValue::VARR);
- for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) {
- std::vector<unsigned char> item = tx.vin[i].scriptWitness.stack[j];
- txinwitness.push_back(HexStr(item.begin(), item.end()));
- }
- in.push_back(Pair("txinwitness", txinwitness));
- }
- in.push_back(Pair("sequence", (int64_t)txin.nSequence));
- vin.push_back(in);
- }
- entry.push_back(Pair("vin", vin));
- UniValue vout(UniValue::VARR);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- const CTxOut& txout = tx.vout[i];
- UniValue out(UniValue::VOBJ);
- out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
- out.push_back(Pair("n", (int64_t)i));
- UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
- out.push_back(Pair("scriptPubKey", o));
- vout.push_back(out);
- }
- entry.push_back(Pair("vout", vout));
+ // Call into TxToUniv() in bitcoin-common to decode the transaction hex.
+ //
+ // Blockchain contextual information (confirmations and blocktime) is not
+ // available to code in bitcoin-common, so we query them here and push the
+ // data into the returned UniValue.
+ TxToUniv(tx, uint256(), entry);
if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
@@ -525,7 +463,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
- TxToJSON(CTransaction(std::move(mtx)), uint256(), result);
+ TxToUniv(CTransaction(std::move(mtx)), uint256(), result);
return result;
}
@@ -565,7 +503,7 @@ UniValue decodescript(const JSONRPCRequest& request)
} else {
// Empty scripts are valid
}
- ScriptPubKeyToJSON(script, r, false);
+ ScriptPubKeyToUniv(script, r, false);
UniValue type;
type = find_value(r, "type");
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 8ecf0bbdac..f4e5313a78 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -247,10 +247,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
- static const CScriptNum bnFalse(0);
- static const CScriptNum bnTrue(1);
+ // static const CScriptNum bnFalse(0);
+ // static const CScriptNum bnTrue(1);
static const valtype vchFalse(0);
- static const valtype vchZero(0);
+ // static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
CScript::const_iterator pc = script.begin();
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 6f47b725fb..7bb8d9941b 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -15,28 +15,6 @@
#include <boost/thread.hpp>
namespace {
-
-/**
- * We're hashing a nonce into the entries themselves, so we don't need extra
- * blinding in the set hash computation.
- *
- * This may exhibit platform endian dependent behavior but because these are
- * nonced hashes (random) and this state is only ever used locally it is safe.
- * All that matters is local consistency.
- */
-class SignatureCacheHasher
-{
-public:
- template <uint8_t hash_select>
- uint32_t operator()(const uint256& key) const
- {
- static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
- uint32_t u;
- std::memcpy(&u, key.begin()+4*hash_select, 4);
- return u;
- }
-};
-
/**
* Valid signature cache, to avoid doing expensive ECDSA signature checking
* twice for every transaction (once when accepted into memory pool, and
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 60690583de..55cec4cc8d 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -19,6 +19,27 @@ static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384;
class CPubKey;
+/**
+ * We're hashing a nonce into the entries themselves, so we don't need extra
+ * blinding in the set hash computation.
+ *
+ * This may exhibit platform endian dependent behavior but because these are
+ * nonced hashes (random) and this state is only ever used locally it is safe.
+ * All that matters is local consistency.
+ */
+class SignatureCacheHasher
+{
+public:
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin()+4*hash_select, 4);
+ return u;
+ }
+};
+
class CachingTransactionSignatureChecker : public TransactionSignatureChecker
{
private:
diff --git a/src/serialize.h b/src/serialize.h
index e4d72d2348..e82ddf2c5a 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -336,11 +336,18 @@ I ReadVarInt(Stream& is)
I n = 0;
while(true) {
unsigned char chData = ser_readdata8(is);
+ if (n > (std::numeric_limits<I>::max() >> 7)) {
+ throw std::ios_base::failure("ReadVarInt(): size too large");
+ }
n = (n << 7) | (chData & 0x7F);
- if (chData & 0x80)
+ if (chData & 0x80) {
+ if (n == std::numeric_limits<I>::max()) {
+ throw std::ios_base::failure("ReadVarInt(): size too large");
+ }
n++;
- else
+ } else {
return n;
+ }
}
}
diff --git a/src/streams.h b/src/streams.h
index 1387b9cf54..8dc5a19ead 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -248,7 +248,8 @@ public:
void insert(iterator it, std::vector<char>::const_iterator first, std::vector<char>::const_iterator last)
{
- assert(last - first >= 0);
+ if (last == first) return;
+ assert(last - first > 0);
if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos)
{
// special case for inserting at the front when there's room
@@ -261,7 +262,8 @@ public:
void insert(iterator it, const char* first, const char* last)
{
- assert(last - first >= 0);
+ if (last == first) return;
+ assert(last - first > 0);
if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos)
{
// special case for inserting at the front when there's room
@@ -339,6 +341,8 @@ public:
void read(char* pch, size_t nSize)
{
+ if (nSize == 0) return;
+
// Read from the beginning of the buffer
unsigned int nReadPosNext = nReadPos + nSize;
if (nReadPosNext >= vch.size())
diff --git a/src/support/cleanse.h b/src/support/cleanse.h
index 3e02aa8fd1..f020216c73 100644
--- a/src/support/cleanse.h
+++ b/src/support/cleanse.h
@@ -8,6 +8,7 @@
#include <stdlib.h>
+// Attempt to overwrite data in the specified memory span.
void memory_cleanse(void *ptr, size_t len);
#endif // BITCOIN_SUPPORT_CLEANSE_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 5d1c5b78d1..3812490ec0 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -84,36 +84,43 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
CNetAddr source = ResolveIP("252.2.2.2");
- // Test 1: Does Addrman respond correctly when empty.
- BOOST_CHECK(addrman.size() == 0);
+ // Test: Does Addrman respond correctly when empty.
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddrInfo addr_null = addrman.Select();
- BOOST_CHECK(addr_null.ToString() == "[::]:0");
+ BOOST_CHECK_EQUAL(addr_null.ToString(), "[::]:0");
- // Test 2: Does Addrman::Add work as expected.
+ // Test: Does Addrman::Add work as expected.
CService addr1 = ResolveService("250.1.1.1", 8333);
- addrman.Add(CAddress(addr1, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source));
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
CAddrInfo addr_ret1 = addrman.Select();
- BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
- // Test 3: Does IP address deduplication work correctly.
+ // Test: Does IP address deduplication work correctly.
// Expected dup IP should not be added.
CService addr1_dup = ResolveService("250.1.1.1", 8333);
- addrman.Add(CAddress(addr1_dup, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK(!addrman.Add(CAddress(addr1_dup, NODE_NONE), source));
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
- // Test 5: New table has one addr and we add a diff addr we should
+ // Test: New table has one addr and we add a diff addr we should
// have two addrs.
CService addr2 = ResolveService("250.1.1.2", 8333);
- addrman.Add(CAddress(addr2, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 2);
+ BOOST_CHECK(addrman.Add(CAddress(addr2, NODE_NONE), source));
+ BOOST_CHECK_EQUAL(addrman.size(), 2);
- // Test 6: AddrMan::Clear() should empty the new table.
+ // Test: AddrMan::Clear() should empty the new table.
addrman.Clear();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddrInfo addr_null2 = addrman.Select();
- BOOST_CHECK(addr_null2.ToString() == "[::]:0");
+ BOOST_CHECK_EQUAL(addr_null2.ToString(), "[::]:0");
+
+ // Test: AddrMan::Add multiple addresses works as expected
+ std::vector<CAddress> vAddr;
+ vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
+ vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
+ BOOST_CHECK(addrman.Add(vAddr, source));
+ BOOST_CHECK_EQUAL(addrman.size(), 2);
}
BOOST_AUTO_TEST_CASE(addrman_ports)
@@ -125,26 +132,26 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
CNetAddr source = ResolveIP("252.2.2.2");
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
// Test 7; Addr with same IP but diff port does not replace existing addr.
CService addr1 = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
CService addr1_port = ResolveService("250.1.1.1", 8334);
addrman.Add(CAddress(addr1_port, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
CAddrInfo addr_ret2 = addrman.Select();
- BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
- // Test 8: Add same IP but diff port to tried table, it doesn't get added.
+ // Test: Add same IP but diff port to tried table, it doesn't get added.
// Perhaps this is not ideal behavior but it is the current behavior.
addrman.Good(CAddress(addr1_port, NODE_NONE));
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
bool newOnly = true;
CAddrInfo addr_ret3 = addrman.Select(newOnly);
- BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
}
@@ -157,25 +164,25 @@ BOOST_AUTO_TEST_CASE(addrman_select)
CNetAddr source = ResolveIP("252.2.2.2");
- // Test 9: Select from new with 1 addr in new.
+ // Test: Select from new with 1 addr in new.
CService addr1 = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
bool newOnly = true;
CAddrInfo addr_ret1 = addrman.Select(newOnly);
- BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
- // Test 10: move addr to tried, select from new expected nothing returned.
+ // Test: move addr to tried, select from new expected nothing returned.
addrman.Good(CAddress(addr1, NODE_NONE));
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
CAddrInfo addr_ret2 = addrman.Select(newOnly);
- BOOST_CHECK(addr_ret2.ToString() == "[::]:0");
+ BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0");
CAddrInfo addr_ret3 = addrman.Select();
- BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
- BOOST_CHECK(addrman.size() == 1);
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
// Add three addresses to new table.
@@ -199,14 +206,15 @@ BOOST_AUTO_TEST_CASE(addrman_select)
addrman.Add(CAddress(addr7, NODE_NONE), ResolveService("250.1.1.3", 8333));
addrman.Good(CAddress(addr7, NODE_NONE));
- // Test 11: 6 addrs + 1 addr from last test = 7.
- BOOST_CHECK(addrman.size() == 7);
+ // Test: 6 addrs + 1 addr from last test = 7.
+ BOOST_CHECK_EQUAL(addrman.size(), 7);
- // Test 12: Select pulls from new and tried regardless of port number.
- BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333");
+ // Test: Select pulls from new and tried regardless of port number.
+ std::set<uint16_t> ports;
+ for (int i = 0; i < 20; ++i) {
+ ports.insert(addrman.Select().GetPort());
+ }
+ BOOST_CHECK_EQUAL(ports.size(), 3);
}
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
@@ -218,24 +226,24 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
CNetAddr source = ResolveIP("252.2.2.2");
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
for (unsigned int i = 1; i < 18; i++) {
CService addr = ResolveService("250.1.1." + boost::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
- //Test 13: No collision in new table yet.
- BOOST_CHECK(addrman.size() == i);
+ //Test: No collision in new table yet.
+ BOOST_CHECK_EQUAL(addrman.size(), i);
}
- //Test 14: new table collision!
+ //Test: new table collision!
CService addr1 = ResolveService("250.1.1.18");
addrman.Add(CAddress(addr1, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 17);
+ BOOST_CHECK_EQUAL(addrman.size(), 17);
CService addr2 = ResolveService("250.1.1.19");
addrman.Add(CAddress(addr2, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 18);
+ BOOST_CHECK_EQUAL(addrman.size(), 18);
}
BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
@@ -247,25 +255,25 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
CNetAddr source = ResolveIP("252.2.2.2");
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
for (unsigned int i = 1; i < 80; i++) {
CService addr = ResolveService("250.1.1." + boost::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
addrman.Good(CAddress(addr, NODE_NONE));
- //Test 15: No collision in tried table yet.
+ //Test: No collision in tried table yet.
BOOST_CHECK_EQUAL(addrman.size(), i);
}
- //Test 16: tried table collision!
+ //Test: tried table collision!
CService addr1 = ResolveService("250.1.1.80");
addrman.Add(CAddress(addr1, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 79);
+ BOOST_CHECK_EQUAL(addrman.size(), 79);
CService addr2 = ResolveService("250.1.1.81");
addrman.Add(CAddress(addr2, NODE_NONE), source);
- BOOST_CHECK(addrman.size() == 80);
+ BOOST_CHECK_EQUAL(addrman.size(), 80);
}
BOOST_AUTO_TEST_CASE(addrman_find)
@@ -275,7 +283,7 @@ BOOST_AUTO_TEST_CASE(addrman_find)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
@@ -288,23 +296,20 @@ BOOST_AUTO_TEST_CASE(addrman_find)
addrman.Add(addr2, source2);
addrman.Add(addr3, source1);
- // Test 17: ensure Find returns an IP matching what we searched on.
+ // Test: ensure Find returns an IP matching what we searched on.
CAddrInfo* info1 = addrman.Find(addr1);
- BOOST_CHECK(info1);
- if (info1)
- BOOST_CHECK(info1->ToString() == "250.1.2.1:8333");
+ BOOST_REQUIRE(info1);
+ BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
// Test 18; Find does not discriminate by port number.
CAddrInfo* info2 = addrman.Find(addr2);
- BOOST_CHECK(info2);
- if (info2 && info1)
- BOOST_CHECK(info2->ToString() == info1->ToString());
+ BOOST_REQUIRE(info2);
+ BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
- // Test 19: Find returns another IP matching what we searched on.
+ // Test: Find returns another IP matching what we searched on.
CAddrInfo* info3 = addrman.Find(addr3);
- BOOST_CHECK(info3);
- if (info3)
- BOOST_CHECK(info3->ToString() == "251.255.2.1:8333");
+ BOOST_REQUIRE(info3);
+ BOOST_CHECK_EQUAL(info3->ToString(), "251.255.2.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_create)
@@ -314,7 +319,7 @@ BOOST_AUTO_TEST_CASE(addrman_create)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
@@ -322,11 +327,11 @@ BOOST_AUTO_TEST_CASE(addrman_create)
int nId;
CAddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
- // Test 20: The result should be the same as the input addr.
- BOOST_CHECK(pinfo->ToString() == "250.1.2.1:8333");
+ // Test: The result should be the same as the input addr.
+ BOOST_CHECK_EQUAL(pinfo->ToString(), "250.1.2.1:8333");
CAddrInfo* info2 = addrman.Find(addr1);
- BOOST_CHECK(info2->ToString() == "250.1.2.1:8333");
+ BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:8333");
}
@@ -337,7 +342,7 @@ BOOST_AUTO_TEST_CASE(addrman_delete)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
@@ -345,10 +350,10 @@ BOOST_AUTO_TEST_CASE(addrman_delete)
int nId;
addrman.Create(addr1, source1, &nId);
- // Test 21: Delete should actually delete the addr.
- BOOST_CHECK(addrman.size() == 1);
+ // Test: Delete should actually delete the addr.
+ BOOST_CHECK_EQUAL(addrman.size(), 1);
addrman.Delete(nId);
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
CAddrInfo* info2 = addrman.Find(addr1);
BOOST_CHECK(info2 == NULL);
}
@@ -360,11 +365,11 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- // Test 22: Sanity check, GetAddr should never return anything if addrman
+ // Test: Sanity check, GetAddr should never return anything if addrman
// is empty.
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK_EQUAL(addrman.size(), 0);
std::vector<CAddress> vAddr1 = addrman.GetAddr();
- BOOST_CHECK(vAddr1.size() == 0);
+ BOOST_CHECK_EQUAL(vAddr1.size(), 0);
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false
@@ -379,7 +384,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
CNetAddr source1 = ResolveIP("250.1.2.1");
CNetAddr source2 = ResolveIP("250.2.3.3");
- // Test 23: Ensure GetAddr works with new addresses.
+ // Test: Ensure GetAddr works with new addresses.
addrman.Add(addr1, source1);
addrman.Add(addr2, source2);
addrman.Add(addr3, source1);
@@ -387,21 +392,20 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
addrman.Add(addr5, source1);
// GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down.
- BOOST_CHECK(addrman.GetAddr().size() == 1);
+ BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1);
- // Test 24: Ensure GetAddr works with new and tried addresses.
+ // Test: Ensure GetAddr works with new and tried addresses.
addrman.Good(CAddress(addr1, NODE_NONE));
addrman.Good(CAddress(addr2, NODE_NONE));
- BOOST_CHECK(addrman.GetAddr().size() == 1);
+ BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1);
- // Test 25: Ensure GetAddr still returns 23% when addrman has many addrs.
+ // Test: Ensure GetAddr still returns 23% when addrman has many addrs.
for (unsigned int i = 1; i < (8 * 256); i++) {
int octet1 = i % 256;
- int octet2 = (i / 256) % 256;
- int octet3 = (i / (256 * 2)) % 256;
- std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + "." + boost::to_string(octet3) + ".23";
+ int octet2 = i >> 8 % 256;
+ std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + ".1.23";
CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE);
-
+
// Ensure that for all addrs in addrman, isTerrible == false.
addr.nTime = GetAdjustedTime();
addrman.Add(addr, ResolveIP(strAddr));
@@ -411,10 +415,10 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
std::vector<CAddress> vAddr = addrman.GetAddr();
size_t percent23 = (addrman.size() * 23) / 100;
- BOOST_CHECK(vAddr.size() == percent23);
- BOOST_CHECK(vAddr.size() == 461);
+ BOOST_CHECK_EQUAL(vAddr.size(), percent23);
+ BOOST_CHECK_EQUAL(vAddr.size(), 461);
// (Addrman.size() < number of addresses added) due to address collisions.
- BOOST_CHECK(addrman.size() == 2007);
+ BOOST_CHECK_EQUAL(addrman.size(), 2006);
}
@@ -437,13 +441,13 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
- BOOST_CHECK(info1.GetTriedBucket(nKey1) == 40);
+ BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1), 40);
- // Test 26: Make sure key actually randomizes bucket placement. A fail on
+ // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue.
BOOST_CHECK(info1.GetTriedBucket(nKey1) != info1.GetTriedBucket(nKey2));
- // Test 27: Two addresses with same IP but different ports can map to
+ // Test: Two addresses with same IP but different ports can map to
// different buckets because they have different keys.
CAddrInfo info2 = CAddrInfo(addr2, source1);
@@ -458,9 +462,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
int bucket = infoi.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
- // Test 28: IP addresses in the same group (\16 prefix for IPv4) should
+ // Test: IP addresses in the same group (\16 prefix for IPv4) should
// never get more than 8 buckets
- BOOST_CHECK(buckets.size() == 8);
+ BOOST_CHECK_EQUAL(buckets.size(), 8);
buckets.clear();
for (int j = 0; j < 255; j++) {
@@ -470,9 +474,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
int bucket = infoj.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
- // Test 29: IP addresses in the different groups should map to more than
+ // Test: IP addresses in the different groups should map to more than
// 8 buckets.
- BOOST_CHECK(buckets.size() == 160);
+ BOOST_CHECK_EQUAL(buckets.size(), 160);
}
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
@@ -492,16 +496,18 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
- BOOST_CHECK(info1.GetNewBucket(nKey1) == 786);
+ // Test: Make sure the buckets are what we expect
+ BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1), 786);
+ BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1), 786);
- // Test 30: Make sure key actually randomizes bucket placement. A fail on
+ // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue.
BOOST_CHECK(info1.GetNewBucket(nKey1) != info1.GetNewBucket(nKey2));
- // Test 31: Ports should not effect bucket placement in the addr
+ // Test: Ports should not effect bucket placement in the addr
CAddrInfo info2 = CAddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey());
- BOOST_CHECK(info1.GetNewBucket(nKey1) == info2.GetNewBucket(nKey1));
+ BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1), info2.GetNewBucket(nKey1));
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
@@ -511,9 +517,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
int bucket = infoi.GetNewBucket(nKey1);
buckets.insert(bucket);
}
- // Test 32: IP addresses in the same group (\16 prefix for IPv4) should
+ // Test: IP addresses in the same group (\16 prefix for IPv4) should
// always map to the same bucket.
- BOOST_CHECK(buckets.size() == 1);
+ BOOST_CHECK_EQUAL(buckets.size(), 1);
buckets.clear();
for (int j = 0; j < 4 * 255; j++) {
@@ -524,7 +530,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
}
- // Test 33: IP addresses in the same source groups should map to no more
+ // Test: IP addresses in the same source groups should map to no more
// than 64 buckets.
BOOST_CHECK(buckets.size() <= 64);
@@ -536,7 +542,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
}
- // Test 34: IP addresses in the different source groups should map to more
+ // Test: IP addresses in the different source groups should map to more
// than 64 buckets.
BOOST_CHECK(buckets.size() > 64);
}
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index fd6f88b366..952cf901f0 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -3,15 +3,23 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
+#include "policy/feerate.h"
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(MoneyRangeTest)
+{
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(-1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(1)), true);
+}
+
BOOST_AUTO_TEST_CASE(GetFeeTest)
{
- CFeeRate feeRate;
+ CFeeRate feeRate, altFeeRate;
feeRate = CFeeRate(0);
// Must always return 0
@@ -53,6 +61,11 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(8), -1); // Special case: returns -1 instead of 0
BOOST_CHECK_EQUAL(feeRate.GetFee(9), -1);
+ // check alternate constructor
+ feeRate = CFeeRate(1000);
+ altFeeRate = CFeeRate(feeRate);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100));
+
// Check full constructor
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
@@ -68,4 +81,28 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
CFeeRate(MAX_MONEY, std::numeric_limits<size_t>::max() >> 1).GetFeePerK();
}
+BOOST_AUTO_TEST_CASE(BinaryOperatorTest)
+{
+ CFeeRate a, b;
+ a = CFeeRate(1);
+ b = CFeeRate(2);
+ BOOST_CHECK(a < b);
+ BOOST_CHECK(b > a);
+ BOOST_CHECK(a == a);
+ BOOST_CHECK(a <= b);
+ BOOST_CHECK(a <= a);
+ BOOST_CHECK(b >= a);
+ BOOST_CHECK(b >= b);
+ // a should be 0.00000002 BTC/kB now
+ a += a;
+ BOOST_CHECK(a == b);
+}
+
+BOOST_AUTO_TEST_CASE(ToStringTest)
+{
+ CFeeRate feeRate;
+ feeRate = CFeeRate(1);
+ BOOST_CHECK_EQUAL(feeRate.ToString(), "0.00000001 BTC/kB");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 4d17417179..72e562808a 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -3,12 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "crypto/aes.h"
+#include "crypto/chacha20.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha512.h"
#include "crypto/hmac_sha256.h"
#include "crypto/hmac_sha512.h"
+#include "random.h"
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"
@@ -187,6 +189,19 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
}
}
+void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ ChaCha20 rng(key.data(), key.size());
+ rng.SetIV(nonce);
+ rng.Seek(seek);
+ std::vector<unsigned char> out = ParseHex(hexout);
+ std::vector<unsigned char> outres;
+ outres.resize(out.size());
+ rng.Output(outres.data(), outres.size());
+ BOOST_CHECK(out == outres);
+}
+
std::string LongTestString(void) {
std::string ret;
for (int i=0; i<200000; i++) {
@@ -439,4 +454,57 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
"b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
}
+
+BOOST_AUTO_TEST_CASE(chacha20_testvector)
+{
+ // Test vector from RFC 7539
+ TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+ "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
+ "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
+ "832c89c167eacd901d7e2bf363");
+
+ // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
+ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
+ "8f41518a11cc387b669b2ee6586");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
+ "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
+ "2b1c43fea817e9ad275ae546963");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
+ "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
+ "62eb7a0433e445f41e3");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
+ "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
+ "97a0b466e7d6bbdb0041b2f586b");
+ TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
+ "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
+ "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
+ "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
+ "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5"
+ "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78"
+ "fab78c9");
+}
+
+BOOST_AUTO_TEST_CASE(countbits_tests)
+{
+ FastRandomContext ctx;
+ for (int i = 0; i <= 64; ++i) {
+ if (i == 0) {
+ // Check handling of zero.
+ BOOST_CHECK_EQUAL(CountBits(0), 0);
+ } else if (i < 10) {
+ for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) {
+ // Exhaustively test up to 10 bits
+ BOOST_CHECK_EQUAL(CountBits(j), i);
+ }
+ } else {
+ for (int k = 0; k < 1000; k++) {
+ // Randomly test 1000 samples of each length above 10 bits.
+ uint64_t j = ((uint64_t)1) << (i - 1) | ctx.randbits(i - 1);
+ BOOST_CHECK_EQUAL(CountBits(j), i);
+ }
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index ccd7155627..8cae4e66e8 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include "cuckoocache.h"
+#include "script/sigcache.h"
#include "test/test_bitcoin.h"
#include "random.h"
#include <thread>
@@ -36,20 +37,6 @@ void insecure_GetRandHash(uint256& t)
*(ptr++) = insecure_rand.rand32();
}
-/** Definition copied from /src/script/sigcache.cpp
- */
-class uint256Hasher
-{
-public:
- template <uint8_t hash_select>
- uint32_t operator()(const uint256& key) const
- {
- static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
- uint32_t u;
- std::memcpy(&u, key.begin() + 4 * hash_select, 4);
- return u;
- }
-};
/* Test that no values not inserted into the cache are read out of it.
@@ -59,7 +46,7 @@ public:
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
insecure_rand = FastRandomContext(true);
- CuckooCache::cache<uint256, uint256Hasher> cc{};
+ CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
uint256 v;
@@ -138,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
double HitRateThresh = 0.98;
size_t megabytes = 4;
for (double load = 0.1; load < 2; load *= 2) {
- double hits = test_cache<CuckooCache::cache<uint256, uint256Hasher>>(megabytes, load);
+ double hits = test_cache<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes, load);
BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh);
}
}
@@ -206,7 +193,7 @@ void test_cache_erase(size_t megabytes)
BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
{
size_t megabytes = 4;
- test_cache_erase<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+ test_cache_erase<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
}
template <typename Cache>
@@ -293,7 +280,7 @@ void test_cache_erase_parallel(size_t megabytes)
BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
{
size_t megabytes = 4;
- test_cache_erase_parallel<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+ test_cache_erase_parallel<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
}
@@ -389,7 +376,7 @@ void test_cache_generations()
}
BOOST_AUTO_TEST_CASE(cuckoocache_generations)
{
- test_cache_generations<CuckooCache::cache<uint256, uint256Hasher>>();
+ test_cache_generations<CuckooCache::cache<uint256, SignatureCacheHasher>>();
}
BOOST_AUTO_TEST_SUITE_END();
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index d52104b4cc..656aec606b 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -39,17 +39,18 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
- TestBlockSubsidyHalvings(Params(CBaseChainParams::MAIN).GetConsensus()); // As in main
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
TestBlockSubsidyHalvings(150); // As in regtest
TestBlockSubsidyHalvings(1000); // Just another interval
}
BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
- const Consensus::Params& consensusParams = Params(CBaseChainParams::MAIN).GetConsensus();
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
CAmount nSum = 0;
for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
- CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
+ CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
BOOST_CHECK(nSubsidy <= 50 * COIN);
nSum += nSubsidy * 1000;
BOOST_CHECK(MoneyRange(nSum));
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 41f42c7b88..fadff612d4 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -194,7 +194,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
// Note that by default, these tests run with size accounting enabled.
- const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
CMutableTransaction tx,tx2;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index b9ed4952bb..0c7f3e5e23 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -11,6 +11,7 @@
#include "net.h"
#include "netbase.h"
#include "chainparams.h"
+#include "util.h"
class CAddrManSerializationMock : public CAddrMan
{
@@ -72,6 +73,18 @@ CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(cnode_listen_port)
+{
+ // test default
+ unsigned short port = GetListenPort();
+ BOOST_CHECK(port == Params().GetDefaultPort());
+ // test set port
+ unsigned short altPort = 12345;
+ SoftSetArg("-port", std::to_string(altPort));
+ port = GetListenPort();
+ BOOST_CHECK(port == altPort);
+}
+
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
CAddrManUncorrupted addrmanUncorrupted;
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index bc2f49ef3f..ed6782ea34 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -16,7 +16,8 @@ BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
{
- CTxMemPool mpool;
+ CBlockPolicyEstimator feeEst;
+ CTxMemPool mpool(&feeEst);
TestMemPoolEntryHelper entry;
CAmount basefee(2000);
CAmount deltaFee(100);
@@ -78,16 +79,16 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// At this point we should need to combine 5 buckets to get enough data points
// So estimateFee(1,2,3) should fail and estimateFee(4) should return somewhere around
// 8*baserate. estimateFee(4) %'s are 100,100,100,100,90 = average 98%
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(2) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(3) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(2) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(3) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
int answerFound;
- BOOST_CHECK(mpool.estimateSmartFee(1, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(3, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(4, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(8, &answerFound) == mpool.estimateFee(8) && answerFound == 8);
+ BOOST_CHECK(feeEst.estimateSmartFee(1, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(3, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(4, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(8, &answerFound, mpool) == feeEst.estimateFee(8) && answerFound == 8);
}
}
@@ -99,7 +100,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Second highest feerate has 100% chance of being included by 2 blocks,
// so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
- origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
+ origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
if (i > 2) { // Fee estimates should be monotonically decreasing
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
}
@@ -118,10 +119,10 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
while (blocknum < 250)
mpool.removeForBlock(block, ++blocknum);
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
@@ -141,8 +142,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
int answerFound;
for (int i = 1; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimateSmartFee(i, &answerFound).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateSmartFee(i, &answerFound, mpool).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
}
// Mine all those transactions
@@ -157,9 +158,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
mpool.removeForBlock(block, 265);
block.clear();
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
// Mine 200 more blocks where everything is mined every block
@@ -179,9 +180,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum);
block.clear();
}
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10; i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
}
// Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
@@ -190,8 +191,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(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.estimateFee(i).GetFeePerK());
- BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateFee(i).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
}
}
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 4ca6f1caf0..3b79f8000d 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -16,69 +16,59 @@ BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup)
/* Test calculation of next difficulty target with no constraints applying */
BOOST_AUTO_TEST_CASE(get_next_work)
{
- SelectParams(CBaseChainParams::MAIN);
- const Consensus::Params& params = Params().GetConsensus();
-
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1261130161; // Block #30240
CBlockIndex pindexLast;
pindexLast.nHeight = 32255;
pindexLast.nTime = 1262152739; // Block #32255
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1d00d86a);
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00d86a);
}
/* Test the constraint on the upper bound for next work */
BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
{
- SelectParams(CBaseChainParams::MAIN);
- const Consensus::Params& params = Params().GetConsensus();
-
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1231006505; // Block #0
CBlockIndex pindexLast;
pindexLast.nHeight = 2015;
pindexLast.nTime = 1233061996; // Block #2015
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1d00ffff);
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00ffff);
}
/* Test the constraint on the lower bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
{
- SelectParams(CBaseChainParams::MAIN);
- const Consensus::Params& params = Params().GetConsensus();
-
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1279008237; // Block #66528
CBlockIndex pindexLast;
pindexLast.nHeight = 68543;
pindexLast.nTime = 1279297671; // Block #68543
pindexLast.nBits = 0x1c05a3f4;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1c0168fd);
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1c0168fd);
}
/* Test the constraint on the upper bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
{
- SelectParams(CBaseChainParams::MAIN);
- const Consensus::Params& params = Params().GetConsensus();
-
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time
CBlockIndex pindexLast;
pindexLast.nHeight = 46367;
pindexLast.nTime = 1269211443; // Block #46367
pindexLast.nBits = 0x1c387f6f;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1d00e1fd);
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00e1fd);
}
BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
{
- SelectParams(CBaseChainParams::MAIN);
- const Consensus::Params& params = Params().GetConsensus();
-
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
std::vector<CBlockIndex> blocks(10000);
for (int i = 0; i < 10000; i++) {
blocks[i].pprev = i ? &blocks[i - 1] : NULL;
blocks[i].nHeight = i;
- blocks[i].nTime = 1269211443 + i * params.nPowTargetSpacing;
+ blocks[i].nTime = 1269211443 + i * chainParams->GetConsensus().nPowTargetSpacing;
blocks[i].nBits = 0x207fffff; /* target 0x7fffff000... */
blocks[i].nChainWork = i ? blocks[i - 1].nChainWork + GetBlockProof(blocks[i - 1]) : arith_uint256(0);
}
@@ -88,7 +78,7 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
CBlockIndex *p2 = &blocks[GetRand(10000)];
CBlockIndex *p3 = &blocks[GetRand(10000)];
- int64_t tdiff = GetBlockProofEquivalentTime(*p1, *p2, *p3, params);
+ int64_t tdiff = GetBlockProofEquivalentTime(*p1, *p2, *p3, chainParams->GetConsensus());
BOOST_CHECK_EQUAL(tdiff, p1->GetBlockTime() - p2->GetBlockTime());
}
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index bd8a7819a4..cfed5e347e 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -28,6 +28,7 @@ class prevector_tester {
typedef typename pretype::size_type Size;
bool passed = true;
FastRandomContext rand_cache;
+ uint256 rand_seed;
template <typename A, typename B>
@@ -183,13 +184,12 @@ public:
}
~prevector_tester() {
- BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
- << rand_cache.Rz
- << ", insecure_rand_Rw: "
- << rand_cache.Rw);
+ BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
}
+
prevector_tester() {
seed_insecure_rand();
+ rand_seed = insecure_rand_seed;
rand_cache = insecure_rand_ctx;
}
};
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index d2c46c0daa..8596734226 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -15,5 +15,39 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
BOOST_CHECK(Random_SanityCheck());
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_CASE(fastrandom_tests)
+{
+ // Check that deterministic FastRandomContexts are deterministic
+ FastRandomContext ctx1(true);
+ FastRandomContext ctx2(true);
+
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64());
+ BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
+ BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7));
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
+
+ // Check that a nondeterministic ones are not
+ FastRandomContext ctx3;
+ FastRandomContext ctx4;
+ BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
+}
+BOOST_AUTO_TEST_CASE(fastrandom_randbits)
+{
+ FastRandomContext ctx1;
+ FastRandomContext ctx2;
+ for (int bits = 0; bits < 63; ++bits) {
+ for (int j = 0; j < 1000; ++j) {
+ uint64_t rangebits = ctx1.randbits(bits);
+ BOOST_CHECK_EQUAL(rangebits >> bits, 0);
+ uint64_t range = ((uint64_t)1) << bits | rangebits;
+ uint64_t rand = ctx2.randrange(range);
+ BOOST_CHECK(rand < range);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index bda3819662..cb625bda11 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -27,7 +27,8 @@
#include <boost/thread.hpp>
-FastRandomContext insecure_rand_ctx(true);
+uint256 insecure_rand_seed = GetRandHash();
+FastRandomContext insecure_rand_ctx(insecure_rand_seed);
extern bool fPrintToConsole;
extern void noui_connect();
diff --git a/src/test/test_random.h b/src/test/test_random.h
index 4a1637ac72..318c44df4d 100644
--- a/src/test/test_random.h
+++ b/src/test/test_random.h
@@ -8,11 +8,17 @@
#include "random.h"
+extern uint256 insecure_rand_seed;
extern FastRandomContext insecure_rand_ctx;
static inline void seed_insecure_rand(bool fDeterministic = false)
{
- insecure_rand_ctx = FastRandomContext(fDeterministic);
+ if (fDeterministic) {
+ insecure_rand_seed = uint256();
+ } else {
+ insecure_rand_seed = GetRandHash();
+ }
+ insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
}
static inline uint32_t insecure_rand(void)
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 5da4907f06..10330c0c23 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -17,8 +17,6 @@
#include <boost/test/unit_test.hpp>
-extern std::map<std::string, std::string> mapArgs;
-
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(util_criticalsection)
@@ -100,52 +98,67 @@ BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat)
BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000");
}
+class TestArgsManager : public ArgsManager
+{
+public:
+ std::map<std::string, std::string>& GetMapArgs()
+ {
+ return mapArgs;
+ };
+ const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs()
+ {
+ return mapMultiArgs;
+ };
+};
+
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
+ TestArgsManager testArgs;
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
- ParseParameters(0, (char**)argv_test);
- BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty());
+ testArgs.ParseParameters(0, (char**)argv_test);
+ BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty());
- ParseParameters(1, (char**)argv_test);
- BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty());
+ testArgs.ParseParameters(1, (char**)argv_test);
+ BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty());
- ParseParameters(5, (char**)argv_test);
+ testArgs.ParseParameters(5, (char**)argv_test);
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
- BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3);
- BOOST_CHECK(IsArgSet("-a") && IsArgSet("-b") && IsArgSet("-ccc")
- && !IsArgSet("f") && !IsArgSet("-d"));
- BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc")
- && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d"));
-
- BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple");
- BOOST_CHECK(mapMultiArgs.at("-ccc").size() == 2);
+ BOOST_CHECK(testArgs.GetMapArgs().size() == 3 && testArgs.GetMapMultiArgs().size() == 3);
+ BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
+ && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
+ BOOST_CHECK(testArgs.GetMapMultiArgs().count("-a") && testArgs.GetMapMultiArgs().count("-b") && testArgs.GetMapMultiArgs().count("-ccc")
+ && !testArgs.GetMapMultiArgs().count("f") && !testArgs.GetMapMultiArgs().count("-d"));
+
+ BOOST_CHECK(testArgs.GetMapArgs()["-a"] == "" && testArgs.GetMapArgs()["-ccc"] == "multiple");
+ BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
}
BOOST_AUTO_TEST_CASE(util_GetArg)
{
- mapArgs.clear();
- mapArgs["strtest1"] = "string...";
+ TestArgsManager testArgs;
+ testArgs.GetMapArgs().clear();
+ testArgs.GetMapArgs()["strtest1"] = "string...";
// strtest2 undefined on purpose
- mapArgs["inttest1"] = "12345";
- mapArgs["inttest2"] = "81985529216486895";
+ testArgs.GetMapArgs()["inttest1"] = "12345";
+ testArgs.GetMapArgs()["inttest2"] = "81985529216486895";
// inttest3 undefined on purpose
- mapArgs["booltest1"] = "";
+ testArgs.GetMapArgs()["booltest1"] = "";
// booltest2 undefined on purpose
- mapArgs["booltest3"] = "0";
- mapArgs["booltest4"] = "1";
-
- BOOST_CHECK_EQUAL(GetArg("strtest1", "default"), "string...");
- BOOST_CHECK_EQUAL(GetArg("strtest2", "default"), "default");
- BOOST_CHECK_EQUAL(GetArg("inttest1", -1), 12345);
- BOOST_CHECK_EQUAL(GetArg("inttest2", -1), 81985529216486895LL);
- BOOST_CHECK_EQUAL(GetArg("inttest3", -1), -1);
- BOOST_CHECK_EQUAL(GetBoolArg("booltest1", false), true);
- BOOST_CHECK_EQUAL(GetBoolArg("booltest2", false), false);
- BOOST_CHECK_EQUAL(GetBoolArg("booltest3", false), false);
- BOOST_CHECK_EQUAL(GetBoolArg("booltest4", false), true);
+ testArgs.GetMapArgs()["booltest3"] = "0";
+ testArgs.GetMapArgs()["booltest4"] = "1";
+
+ BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
+ BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
+ BOOST_CHECK_EQUAL(testArgs.GetArg("inttest1", -1), 12345);
+ BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL);
+ BOOST_CHECK_EQUAL(testArgs.GetArg("inttest3", -1), -1);
+ BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
+ BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false);
+ BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false);
+ BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true);
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index e2b5573abd..79405ec4d1 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -209,7 +209,8 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
// Sanity checks of version bit deployments
- const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus();
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const Consensus::Params &mainnetParams = chainParams->GetConsensus();
for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i);
// Make sure that no deployment tries to set an invalid bit.
@@ -235,7 +236,8 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
- const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus();
+ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const Consensus::Params &mainnetParams = chainParams->GetConsensus();
// Use the TESTDUMMY deployment for testing purposes.
int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 0794a3902f..ac842da6bf 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -5,7 +5,6 @@
#include "txmempool.h"
-#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "validation.h"
@@ -16,7 +15,6 @@
#include "util.h"
#include "utilmoneystr.h"
#include "utiltime.h"
-#include "version.h"
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -333,8 +331,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
assert(int(nSigOpCostWithAncestors) >= 0);
}
-CTxMemPool::CTxMemPool() :
- nTransactionsUpdated(0)
+CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) :
+ nTransactionsUpdated(0), minerPolicyEstimator(estimator)
{
_clear(); //lock free clear
@@ -342,13 +340,6 @@ CTxMemPool::CTxMemPool() :
// accepting transactions becomes O(N^2) where N is the number
// of transactions in the pool
nCheckFrequency = 0;
-
- minerPolicyEstimator = new CBlockPolicyEstimator();
-}
-
-CTxMemPool::~CTxMemPool()
-{
- delete minerPolicyEstimator;
}
void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
@@ -427,7 +418,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
- minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
+ if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);}
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
@@ -457,7 +448,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
mapLinks.erase(it);
mapTx.erase(it);
nTransactionsUpdated++;
- minerPolicyEstimator->removeTx(hash);
+ if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash);}
}
// Calculates descendants of entry that are not already in setDescendants, and adds to
@@ -591,7 +582,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
entries.push_back(&*i);
}
// Before the txs in the new block have been removed from the mempool, update policy estimates
- minerPolicyEstimator->processBlock(nBlockHeight, entries);
+ if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
for (const auto& tx : vtx)
{
txiter it = mapTx.find(tx->GetHash());
@@ -850,51 +841,6 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
return GetInfo(i);
}
-CFeeRate CTxMemPool::estimateFee(int nBlocks) const
-{
- LOCK(cs);
- return minerPolicyEstimator->estimateFee(nBlocks);
-}
-CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, int *answerFoundAtBlocks) const
-{
- LOCK(cs);
- return minerPolicyEstimator->estimateSmartFee(nBlocks, answerFoundAtBlocks, *this);
-}
-
-bool
-CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
-{
- try {
- LOCK(cs);
- fileout << 139900; // version required to read: 0.13.99 or later
- fileout << CLIENT_VERSION; // version that wrote the file
- minerPolicyEstimator->Write(fileout);
- }
- catch (const std::exception&) {
- LogPrintf("CTxMemPool::WriteFeeEstimates(): unable to write policy estimator data (non-fatal)\n");
- return false;
- }
- return true;
-}
-
-bool
-CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
-{
- try {
- int nVersionRequired, nVersionThatWrote;
- filein >> nVersionRequired >> nVersionThatWrote;
- if (nVersionRequired > CLIENT_VERSION)
- return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired);
- LOCK(cs);
- minerPolicyEstimator->Read(filein, nVersionThatWrote);
- }
- catch (const std::exception&) {
- LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n");
- return false;
- }
- return true;
-}
-
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
{
{
diff --git a/src/txmempool.h b/src/txmempool.h
index 4222789510..94987ef4d1 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -16,6 +16,7 @@
#include "amount.h"
#include "coins.h"
#include "indirectmap.h"
+#include "policy/feerate.h"
#include "primitives/transaction.h"
#include "sync.h"
#include "random.h"
@@ -496,8 +497,7 @@ public:
/** Create a new CTxMemPool.
*/
- CTxMemPool();
- ~CTxMemPool();
+ CTxMemPool(CBlockPolicyEstimator* estimator = nullptr);
/**
* If sanity-checking is turned on, check makes sure the pool is
@@ -618,19 +618,6 @@ public:
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
- /** Estimate fee rate needed to get into the next nBlocks
- * If no answer can be given at nBlocks, return an estimate
- * at the lowest number of blocks where one can be given
- */
- CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks = NULL) const;
-
- /** Estimate fee rate needed to get into the next nBlocks */
- CFeeRate estimateFee(int nBlocks) const;
-
- /** Write/Read estimates to disk */
- bool WriteFeeEstimates(CAutoFile& fileout) const;
- bool ReadFeeEstimates(CAutoFile& filein);
-
size_t DynamicMemoryUsage() const;
boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded;
diff --git a/src/util.cpp b/src/util.cpp
index 5473799289..2f0f846291 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -13,7 +13,6 @@
#include "fs.h"
#include "random.h"
#include "serialize.h"
-#include "sync.h"
#include "utilstrencodings.h"
#include "utiltime.h"
@@ -88,27 +87,11 @@
#include <openssl/rand.h>
#include <openssl/conf.h>
-// Work around clang compilation problem in Boost 1.46:
-// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
-// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options
-// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION
-namespace boost {
-
- namespace program_options {
- std::string to_internal(const std::string&);
- }
-
-} // namespace boost
-
-
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
-CCriticalSection cs_args;
-std::map<std::string, std::string> mapArgs;
-static std::map<std::string, std::vector<std::string> > _mapMultiArgs;
-const std::map<std::string, std::vector<std::string> >& mapMultiArgs = _mapMultiArgs;
+ArgsManager gArgs;
bool fPrintToConsole = false;
bool fPrintToDebugLog = true;
@@ -118,7 +101,7 @@ bool fLogIPs = DEFAULT_LOGIPS;
std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface;
-/** Log categories bitfield. Leveldb/libevent need special handling if their flags are changed at runtime. */
+/** Log categories bitfield. */
std::atomic<uint32_t> logCategories(0);
/** Init OpenSSL library multithreading support */
@@ -295,6 +278,21 @@ std::string ListLogCategories()
return ret;
}
+std::vector<CLogCategoryActive> ListActiveLogCategories()
+{
+ std::vector<CLogCategoryActive> ret;
+ for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
+ // Omit the special cases.
+ if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
+ CLogCategoryActive catActive;
+ catActive.category = LogCategories[i].category;
+ catActive.active = LogAcceptCategory(LogCategories[i].flag);
+ ret.push_back(catActive);
+ }
+ }
+ return ret;
+}
+
/**
* fStartedNewLine is a state variable held by the calling context that will
* suppress printing of the timestamp when multiple calls are made that don't
@@ -382,11 +380,11 @@ static void InterpretNegativeSetting(std::string& strKey, std::string& strValue)
}
}
-void ParseParameters(int argc, const char* const argv[])
+void ArgsManager::ParseParameters(int argc, const char* const argv[])
{
LOCK(cs_args);
mapArgs.clear();
- _mapMultiArgs.clear();
+ mapMultiArgs.clear();
for (int i = 1; i < argc; i++)
{
@@ -414,17 +412,23 @@ void ParseParameters(int argc, const char* const argv[])
InterpretNegativeSetting(str, strValue);
mapArgs[str] = strValue;
- _mapMultiArgs[str].push_back(strValue);
+ mapMultiArgs[str].push_back(strValue);
}
}
-bool IsArgSet(const std::string& strArg)
+std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg)
+{
+ LOCK(cs_args);
+ return mapMultiArgs.at(strArg);
+}
+
+bool ArgsManager::IsArgSet(const std::string& strArg)
{
LOCK(cs_args);
return mapArgs.count(strArg);
}
-std::string GetArg(const std::string& strArg, const std::string& strDefault)
+std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
@@ -432,7 +436,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault)
return strDefault;
}
-int64_t GetArg(const std::string& strArg, int64_t nDefault)
+int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
@@ -440,7 +444,7 @@ int64_t GetArg(const std::string& strArg, int64_t nDefault)
return nDefault;
}
-bool GetBoolArg(const std::string& strArg, bool fDefault)
+bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
@@ -448,16 +452,16 @@ bool GetBoolArg(const std::string& strArg, bool fDefault)
return fDefault;
}
-bool SoftSetArg(const std::string& strArg, const std::string& strValue)
+bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return false;
- mapArgs[strArg] = strValue;
+ ForceSetArg(strArg, strValue);
return true;
}
-bool SoftSetBoolArg(const std::string& strArg, bool fValue)
+bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
{
if (fValue)
return SoftSetArg(strArg, std::string("1"));
@@ -465,10 +469,11 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue)
return SoftSetArg(strArg, std::string("0"));
}
-void ForceSetArg(const std::string& strArg, const std::string& strValue)
+void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
mapArgs[strArg] = strValue;
+ mapMultiArgs[strArg].push_back(strValue);
}
@@ -587,7 +592,7 @@ fs::path GetConfigFile(const std::string& confPath)
return pathConfigFile;
}
-void ReadConfigFile(const std::string& confPath)
+void ArgsManager::ReadConfigFile(const std::string& confPath)
{
fs::ifstream streamConfig(GetConfigFile(confPath));
if (!streamConfig.good())
@@ -606,7 +611,7 @@ void ReadConfigFile(const std::string& confPath)
InterpretNegativeSetting(strKey, strValue);
if (mapArgs.count(strKey) == 0)
mapArgs[strKey] = strValue;
- _mapMultiArgs[strKey].push_back(strValue);
+ mapMultiArgs[strKey].push_back(strValue);
}
}
// If datadir is changed in .conf file:
diff --git a/src/util.h b/src/util.h
index 7998449fee..229478d835 100644
--- a/src/util.h
+++ b/src/util.h
@@ -16,6 +16,7 @@
#include "compat.h"
#include "fs.h"
+#include "sync.h"
#include "tinyformat.h"
#include "utiltime.h"
@@ -41,7 +42,6 @@ public:
boost::signals2::signal<std::string (const char* psz)> Translate;
};
-extern const std::map<std::string, std::vector<std::string> >& mapMultiArgs;
extern bool fPrintToConsole;
extern bool fPrintToDebugLog;
@@ -69,6 +69,12 @@ inline std::string _(const char* psz)
void SetupEnvironment();
bool SetupNetworking();
+struct CLogCategoryActive
+{
+ std::string category;
+ bool active;
+};
+
namespace BCLog {
enum LogFlags : uint32_t {
NONE = 0,
@@ -102,9 +108,12 @@ static inline bool LogAcceptCategory(uint32_t category)
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
}
-/** Returns a string with the supported log categories */
+/** Returns a string with the log categories. */
std::string ListLogCategories();
+/** Returns a vector of the active log categories. */
+std::vector<CLogCategoryActive> ListActiveLogCategories();
+
/** Return true if str parses as a log category and set the flags in f */
bool GetLogCategory(uint32_t *f, const std::string *str);
@@ -139,7 +148,6 @@ bool error(const char* fmt, const Args&... args)
}
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
-void ParseParameters(int argc, const char*const argv[]);
void FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
@@ -154,7 +162,6 @@ fs::path GetConfigFile(const std::string& confPath);
fs::path GetPidFile();
void CreatePidFile(const fs::path &path, pid_t pid);
#endif
-void ReadConfigFile(const std::string& confPath);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
@@ -171,6 +178,16 @@ inline bool IsSwitchChar(char c)
#endif
}
+class ArgsManager
+{
+protected:
+ CCriticalSection cs_args;
+ std::map<std::string, std::string> mapArgs;
+ std::map<std::string, std::vector<std::string> > mapMultiArgs;
+public:
+ void ParseParameters(int argc, const char*const argv[]);
+ void ReadConfigFile(const std::string& confPath);
+ std::vector<std::string> GetArgs(const std::string& strArg);
/**
* Return true if the given argument has been manually set
*
@@ -226,6 +243,55 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue);
// Forces a arg setting, used only in testing
void ForceSetArg(const std::string& strArg, const std::string& strValue);
+};
+
+extern ArgsManager gArgs;
+
+// wrappers using the global ArgsManager:
+static inline void ParseParameters(int argc, const char*const argv[])
+{
+ gArgs.ParseParameters(argc, argv);
+}
+
+static inline void ReadConfigFile(const std::string& confPath)
+{
+ gArgs.ReadConfigFile(confPath);
+}
+
+static inline bool SoftSetArg(const std::string& strArg, const std::string& strValue)
+{
+ return gArgs.SoftSetArg(strArg, strValue);
+}
+
+static inline void ForceSetArg(const std::string& strArg, const std::string& strValue)
+{
+ gArgs.ForceSetArg(strArg, strValue);
+}
+
+static inline bool IsArgSet(const std::string& strArg)
+{
+ return gArgs.IsArgSet(strArg);
+}
+
+static inline std::string GetArg(const std::string& strArg, const std::string& strDefault)
+{
+ return gArgs.GetArg(strArg, strDefault);
+}
+
+static inline int64_t GetArg(const std::string& strArg, int64_t nDefault)
+{
+ return gArgs.GetArg(strArg, nDefault);
+}
+
+static inline bool GetBoolArg(const std::string& strArg, bool fDefault)
+{
+ return gArgs.GetBoolArg(strArg, fDefault);
+}
+
+static inline bool SoftSetBoolArg(const std::string& strArg, bool fValue)
+{
+ return gArgs.SoftSetBoolArg(strArg, fValue);
+}
/**
* Format a string to be used as group of options in help messages
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index a9936a645a..510f540b1d 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -9,14 +9,17 @@
#include "utiltime.h"
+#include <atomic>
+
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
-static int64_t nMockTime = 0; //!< For unit testing
+static std::atomic<int64_t> nMockTime(0); //!< For unit testing
int64_t GetTime()
{
- if (nMockTime) return nMockTime;
+ int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
+ if (mocktime) return mocktime;
time_t now = time(NULL);
assert(now > 0);
@@ -25,7 +28,7 @@ int64_t GetTime()
void SetMockTime(int64_t nMockTimeIn)
{
- nMockTime = nMockTimeIn;
+ nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}
int64_t GetTimeMillis()
@@ -52,7 +55,8 @@ int64_t GetSystemTimeInSeconds()
/** Return a time useful for the debug log */
int64_t GetLogTimeMicros()
{
- if (nMockTime) return nMockTime*1000000;
+ int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
+ if (mocktime) return mocktime*1000000;
return GetTimeMicros();
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 35b957a451..75a35756d4 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -6,6 +6,7 @@
#include "validation.h"
#include "arith_uint256.h"
+#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -80,7 +81,8 @@ uint256 hashAssumeValid;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
-CTxMemPool mempool;
+CBlockPolicyEstimator feeEstimator;
+CTxMemPool mempool(&feeEstimator);
static void CheckBlockIndex(const Consensus::Params& consensusParams);
@@ -154,39 +156,6 @@ namespace {
std::set<int> setDirtyFileInfo;
} // anon namespace
-/* Use this class to start tracking transactions that are removed from the
- * mempool and pass all those transactions through SyncTransaction when the
- * object goes out of scope. This is currently only used to call SyncTransaction
- * on conflicts removed from the mempool during block connection. Applied in
- * ActivateBestChain around ActivateBestStep which in turn calls:
- * ConnectTip->removeForBlock->removeConflicts
- */
-class MemPoolConflictRemovalTracker
-{
-private:
- std::vector<CTransactionRef> conflictedTxs;
- CTxMemPool &pool;
-
-public:
- MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) {
- pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- }
-
- void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
- if (reason == MemPoolRemovalReason::CONFLICT) {
- conflictedTxs.push_back(txRemoved);
- }
- }
-
- ~MemPoolConflictRemovalTracker() {
- pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- for (const auto& tx : conflictedTxs) {
- GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
- conflictedTxs.clear();
- }
-};
-
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
@@ -982,7 +951,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
+ GetMainSignals().TransactionAddedToMempool(ptx);
return true;
}
@@ -1330,10 +1299,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
- if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
- return false;
- }
- return true;
+ return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error);
}
int GetSpendHeight(const CCoinsViewCache& inputs)
@@ -1557,24 +1523,36 @@ bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint
return fClean;
}
-bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
+enum DisconnectResult
{
- assert(pindex->GetBlockHash() == view.GetBestBlock());
+ DISCONNECT_OK, // All good.
+ DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
+ DISCONNECT_FAILED // Something else went wrong.
+};
- if (pfClean)
- *pfClean = false;
+/** 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. */
+static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
+{
+ assert(pindex->GetBlockHash() == view.GetBestBlock());
bool fClean = true;
CBlockUndo blockUndo;
CDiskBlockPos pos = pindex->GetUndoPos();
- if (pos.IsNull())
- return error("DisconnectBlock(): no undo data available");
- if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash()))
- return error("DisconnectBlock(): failure reading undo data");
+ if (pos.IsNull()) {
+ error("DisconnectBlock(): no undo data available");
+ return DISCONNECT_FAILED;
+ }
+ if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) {
+ error("DisconnectBlock(): failure reading undo data");
+ return DISCONNECT_FAILED;
+ }
- if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
- return error("DisconnectBlock(): block and undo data inconsistent");
+ if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) {
+ error("DisconnectBlock(): block and undo data inconsistent");
+ return DISCONNECT_FAILED;
+ }
// undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) {
@@ -1603,8 +1581,10 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
// restore inputs
if (i > 0) { // not coinbases
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
- if (txundo.vprevout.size() != tx.vin.size())
- return error("DisconnectBlock(): transaction and undo data inconsistent");
+ if (txundo.vprevout.size() != tx.vin.size()) {
+ error("DisconnectBlock(): transaction and undo data inconsistent");
+ return DISCONNECT_FAILED;
+ }
for (unsigned int j = tx.vin.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
const CTxInUndo &undo = txundo.vprevout[j];
@@ -1617,12 +1597,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
- if (pfClean) {
- *pfClean = fClean;
- return true;
- }
-
- return fClean;
+ return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
void static FlushBlockFile(bool fFinalize = false)
@@ -1710,8 +1685,11 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
-bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
+/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
+ * Validity checks that depend on the UTXO set are also done; ConnectBlock()
+ * can fail if those validity checks fail (among other reasons). */
+static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false)
{
AssertLockHeld(cs_main);
assert(pindex);
@@ -1950,12 +1928,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001);
- // Watch for changes to the previous coinbase transaction.
- static uint256 hashPrevBestCoinBase;
- GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = block.vtx[0]->GetHash();
-
-
int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001);
@@ -2153,14 +2125,15 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
// Read block from disk.
- CBlock block;
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ CBlock& block = *pblock;
if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip);
- if (!DisconnectBlock(block, state, pindexDelete, view))
+ if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
bool flushed = view.Flush();
assert(flushed);
@@ -2195,9 +2168,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
- for (const auto& tx : block.vtx) {
- GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
+ GetMainSignals().BlockDisconnected(pblock);
return true;
}
@@ -2207,36 +2178,92 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
+struct PerBlockConnectTrace {
+ CBlockIndex* pindex = NULL;
+ std::shared_ptr<const CBlock> pblock;
+ std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
+ PerBlockConnectTrace() : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
+};
/**
* Used to track blocks whose transactions were applied to the UTXO state as a
* part of a single ActivateBestChainStep call.
+ *
+ * This class also tracks transactions that are removed from the mempool as
+ * conflicts (per block) and can be used to pass all those transactions
+ * through SyncTransaction.
+ *
+ * This class assumes (and asserts) that the conflicted transactions for a given
+ * block are added via mempool callbacks prior to the BlockConnected() associated
+ * with those transactions. If any transactions are marked conflicted, it is
+ * assumed that an associated block will always be added.
+ *
+ * This class is single-use, once you call GetBlocksConnected() you have to throw
+ * it away and make a new one.
*/
-struct ConnectTrace {
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
+class ConnectTrace {
+private:
+ std::vector<PerBlockConnectTrace> blocksConnected;
+ CTxMemPool &pool;
+
+public:
+ ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
+ pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ ~ConnectTrace() {
+ pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
+ assert(!blocksConnected.back().pindex);
+ assert(pindex);
+ assert(pblock);
+ blocksConnected.back().pindex = pindex;
+ blocksConnected.back().pblock = std::move(pblock);
+ blocksConnected.emplace_back();
+ }
+
+ std::vector<PerBlockConnectTrace>& GetBlocksConnected() {
+ // We always keep one extra block at the end of our list because
+ // blocks are added after all the conflicted transactions have
+ // been filled in. Thus, the last entry should always be an empty
+ // one waiting for the transactions from the next block. We pop
+ // the last entry here to make sure the list we return is sane.
+ assert(!blocksConnected.back().pindex);
+ assert(blocksConnected.back().conflictedTxs->empty());
+ blocksConnected.pop_back();
+ return blocksConnected;
+ }
+
+ void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
+ assert(!blocksConnected.back().pindex);
+ if (reason == MemPoolRemovalReason::CONFLICT) {
+ blocksConnected.back().conflictedTxs->emplace_back(std::move(txRemoved));
+ }
+ }
};
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*
- * The block is always added to connectTrace (either after loading from disk or by copying
- * pblock) - if that is not intended, care must be taken to remove the last entry in
- * blocksConnected in case of failure.
+ * The block is added to connectTrace if connection succeeds.
*/
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
+ std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
- connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
+ pthisBlock = pblockNew;
} else {
- connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
+ pthisBlock = pblock;
}
- const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
+ const CBlock& blockConnecting = *pthisBlock;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
@@ -2270,6 +2297,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
+
+ connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
return true;
}
@@ -2388,8 +2417,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
state = CValidationState();
fInvalidFound = true;
fContinue = false;
- // If we didn't actually connect the block, don't notify listeners about it
- connectTrace.blocksConnected.pop_back();
break;
} else {
// A system error occurred (disk space, database error, ...).
@@ -2461,18 +2488,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break;
const CBlockIndex *pindexFork;
- ConnectTrace connectTrace;
bool fInitialDownload;
{
LOCK(cs_main);
- { // TODO: Temporarily ensure that mempool removals are notified before
- // connected transactions. This shouldn't matter, but the abandoned
- // state of transactions in our wallet is currently cleared when we
- // receive another notification and there is a race condition where
- // notification of a connected conflict might cause an outside process
- // to abandon a transaction and then have it inadvertently cleared by
- // the notification that the conflicted transaction was evicted.
- MemPoolConflictRemovalTracker mrt(mempool);
+ ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+
CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == NULL) {
pindexMostWork = FindMostWorkChain();
@@ -2495,16 +2515,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
- // throw all transactions though the signal-interface
-
- } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified
-
- // Transactions in the connected block are notified
- for (const auto& pair : connectTrace.blocksConnected) {
- assert(pair.second);
- const CBlock& block = *(pair.second);
- for (unsigned int i = 0; i < block.vtx.size(); i++)
- GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
+ assert(trace.pblock && trace.pindex);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs);
}
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@@ -2526,6 +2539,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return false;
}
+ int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
+ if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
+
return true;
}
@@ -2668,7 +2684,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos)
+static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2676,7 +2692,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
- if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) {
+ if (IsWitnessEnabled(pindexNew->pprev, consensusParams)) {
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
@@ -3198,7 +3214,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
AbortNode(state, "Failed to write block");
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
+ if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("AcceptBlock(): ReceivedBlockTransactions failed");
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
@@ -3645,15 +3661,17 @@ 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) {
- bool fClean = true;
- if (!DisconnectBlock(block, state, pindex, coins, &fClean))
+ 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());
+ }
pindexState = pindex->pprev;
- if (!fClean) {
+ if (res == DISCONNECT_UNCLEAN) {
nGoodTransactions = 0;
pindexFailure = pindex;
- } else
+ } else {
nGoodTransactions += block.vtx.size();
+ }
}
if (ShutdownRequested())
return true;
@@ -3829,7 +3847,7 @@ bool InitBlockIndex(const CChainParams& chainparams)
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
return error("LoadBlockIndex(): writing genesis block to disk failed");
CBlockIndex *pindex = AddToBlockIndex(block);
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
+ 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(state, FLUSH_STATE_ALWAYS);
diff --git a/src/validation.h b/src/validation.h
index 4aa10cbb0b..8ddceb2306 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -11,10 +11,10 @@
#endif
#include "amount.h"
-#include "chain.h"
#include "coins.h"
#include "fs.h"
#include "protocol.h" // For CMessageHeader::MessageStartChars
+#include "policy/feerate.h"
#include "script/script_error.h"
#include "sync.h"
#include "versionbits.h"
@@ -39,6 +39,7 @@ class CChainParams;
class CInv;
class CConnman;
class CScriptCheck;
+class CBlockPolicyEstimator;
class CTxMemPool;
class CValidationInterface;
class CValidationState;
@@ -131,7 +132,8 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
-
+/** Default for -persistmempool */
+static const bool DEFAULT_PERSIST_MEMPOOL = true;
/** Default for -mempoolreplacement */
static const bool DEFAULT_ENABLE_REPLACEMENT = true;
/** Default for using fee filter */
@@ -145,6 +147,9 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
static const bool DEFAULT_PEERBLOOMFILTERS = true;
+/** Default for -stopatheight */
+static const int DEFAULT_STOPATHEIGHT = 0;
+
struct BlockHasher
{
size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
@@ -152,6 +157,7 @@ struct BlockHasher
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
+extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap mapBlockIndex;
@@ -483,18 +489,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
-/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
- * Validity checks that depend on the UTXO set are also done; ConnectBlock()
- * can fail if those validity checks fail (among other reasons). */
-bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins,
- const CChainParams& chainparams, bool fJustCheck = false);
-
-/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
- * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
- * will be true if no problems were found. Otherwise, the return value will be false in case
- * of problems. Note that in any case, coins may be modified. */
-bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
-
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index d4121a28bc..46d7c9b329 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -14,39 +14,39 @@ CMainSignals& GetMainSignals()
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
- g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
+ 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.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
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.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
- g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
+ 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));
}
void UnregisterAllValidationInterfaces() {
- g_signals.BlockFound.disconnect_all_slots();
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.UpdatedTransaction.disconnect_all_slots();
- g_signals.SyncTransaction.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();
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 7f13a29d23..460aecf243 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -7,16 +7,16 @@
#define BITCOIN_VALIDATIONINTERFACE_H
#include <boost/signals2/signal.hpp>
-#include <boost/shared_ptr.hpp>
#include <memory>
+#include "primitives/transaction.h" // CTransaction(Ref)
+
class CBlock;
class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
class CConnman;
class CReserveScript;
-class CTransaction;
class CValidationInterface;
class CValidationState;
class uint256;
@@ -33,14 +33,14 @@ void UnregisterAllValidationInterfaces();
class CValidationInterface {
protected:
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
- virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
+ 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 UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
- virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {};
- virtual void ResetRequestCount(const uint256 &hash) {};
+ 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*);
@@ -50,19 +50,15 @@ protected:
struct CMainSignals {
/** Notifies listeners of updated block chain tip */
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
- /** A posInBlock value for SyncTransaction calls for transactions not
- * included in connected blocks such as transactions removed from mempool,
- * accepted to mempool or appearing in disconnected blocks.*/
- static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1;
- /** Notifies listeners of updated transaction data (transaction, and
- * optionally the block it is found in). Called with block data when
- * transaction is included in a connected block, and without block data when
- * transaction was accepted to mempool, removed from mempool (only when
- * removal was due to conflict from connected block), or appeared in a
- * disconnected block.*/
- boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
- /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
- boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
+ /** Notifies listeners of a transaction having been added to mempool. */
+ boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
+ /**
+ * 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;
+ /** 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;
/** Notifies listeners about an inventory item being seen on the network. */
@@ -77,9 +73,7 @@ struct CMainSignals {
*/
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
/** Notifies listeners that a key for mining is required (coinbase) */
- boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining;
- /** Notifies listeners that a block has been successfully mined */
- boost::signals2::signal<void (const uint256 &)> BlockFound;
+ boost::signals2::signal<void (std::shared_ptr<CReserveScript>&)> ScriptForMining;
/**
* 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 */
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 4e93e929be..2aa26fb00a 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_WALLET_COINCONTROL_H
#define BITCOIN_WALLET_COINCONTROL_H
+#include "policy/feerate.h"
#include "primitives/transaction.h"
#include "wallet/wallet.h"
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index f47fc92b57..25f6bdd9d9 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -359,13 +359,16 @@ void CDBEnv::CheckpointLSN(const std::string& strFile)
}
-CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
+CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
{
int ret;
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
fFlushOnClose = fFlushOnCloseIn;
- if (strFilename.empty())
+ env = dbw.env;
+ if (dbw.IsDummy()) {
return;
+ }
+ const std::string &strFilename = dbw.strFile;
bool fCreate = strchr(pszMode, 'c') != NULL;
unsigned int nFlags = DB_THREAD;
@@ -373,17 +376,17 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
nFlags |= DB_CREATE;
{
- LOCK(bitdb.cs_db);
- if (!bitdb.Open(GetDataDir()))
+ LOCK(env->cs_db);
+ if (!env->Open(GetDataDir()))
throw std::runtime_error("CDB: Failed to open database environment.");
strFile = strFilename;
- ++bitdb.mapFileUseCount[strFile];
- pdb = bitdb.mapDb[strFile];
+ ++env->mapFileUseCount[strFile];
+ pdb = env->mapDb[strFile];
if (pdb == NULL) {
- pdb = new Db(bitdb.dbenv, 0);
+ pdb = new Db(env->dbenv, 0);
- bool fMockDb = bitdb.IsMock();
+ bool fMockDb = env->IsMock();
if (fMockDb) {
DbMpoolFile* mpf = pdb->get_mpf();
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
@@ -401,7 +404,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
if (ret != 0) {
delete pdb;
pdb = NULL;
- --bitdb.mapFileUseCount[strFile];
+ --env->mapFileUseCount[strFile];
strFile = "";
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
@@ -413,7 +416,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
fReadOnly = fTmp;
}
- bitdb.mapDb[strFile] = pdb;
+ env->mapDb[strFile] = pdb;
}
}
}
@@ -428,7 +431,7 @@ void CDB::Flush()
if (fReadOnly)
nMinutes = 1;
- bitdb.dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ env->dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
}
void CDB::Close()
@@ -444,8 +447,8 @@ void CDB::Close()
Flush();
{
- LOCK(bitdb.cs_db);
- --bitdb.mapFileUseCount[strFile];
+ LOCK(env->cs_db);
+ --env->mapFileUseCount[strFile];
}
}
@@ -463,32 +466,28 @@ void CDBEnv::CloseDb(const std::string& strFile)
}
}
-bool CDBEnv::RemoveDb(const std::string& strFile)
-{
- this->CloseDb(strFile);
-
- LOCK(cs_db);
- int rc = dbenv->dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
- return (rc == 0);
-}
-
-bool CDB::Rewrite(const std::string& strFile, const char* pszSkip)
+bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
{
+ if (dbw.IsDummy()) {
+ return true;
+ }
+ CDBEnv *env = dbw.env;
+ const std::string& strFile = dbw.strFile;
while (true) {
{
- LOCK(bitdb.cs_db);
- if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) {
+ LOCK(env->cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
// Flush log data to the dat file
- bitdb.CloseDb(strFile);
- bitdb.CheckpointLSN(strFile);
- bitdb.mapFileUseCount.erase(strFile);
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
bool fSuccess = true;
LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile);
std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {}
- CDB db(strFile.c_str(), "r");
- Db* pdbCopy = new Db(bitdb.dbenv, 0);
+ CDB db(dbw, "r");
+ Db* pdbCopy = new Db(env->dbenv, 0);
int ret = pdbCopy->open(NULL, // Txn pointer
strFileRes.c_str(), // Filename
@@ -531,17 +530,17 @@ bool CDB::Rewrite(const std::string& strFile, const char* pszSkip)
}
if (fSuccess) {
db.Close();
- bitdb.CloseDb(strFile);
+ env->CloseDb(strFile);
if (pdbCopy->close(0))
fSuccess = false;
delete pdbCopy;
}
}
if (fSuccess) {
- Db dbA(bitdb.dbenv, 0);
+ Db dbA(env->dbenv, 0);
if (dbA.remove(strFile.c_str(), NULL, 0))
fSuccess = false;
- Db dbB(bitdb.dbenv, 0);
+ Db dbB(env->dbenv, 0);
if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
fSuccess = false;
}
@@ -596,16 +595,21 @@ void CDBEnv::Flush(bool fShutdown)
}
}
-bool CDB::PeriodicFlush(std::string strFile)
+bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
{
+ if (dbw.IsDummy()) {
+ return true;
+ }
bool ret = false;
+ CDBEnv *env = dbw.env;
+ const std::string& strFile = dbw.strFile;
TRY_LOCK(bitdb.cs_db,lockDb);
if (lockDb)
{
// Don't do this if any databases are in use
int nRefCount = 0;
- std::map<std::string, int>::iterator mit = bitdb.mapFileUseCount.begin();
- while (mit != bitdb.mapFileUseCount.end())
+ std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
+ while (mit != env->mapFileUseCount.end())
{
nRefCount += (*mit).second;
mit++;
@@ -614,17 +618,17 @@ bool CDB::PeriodicFlush(std::string strFile)
if (nRefCount == 0)
{
boost::this_thread::interruption_point();
- std::map<std::string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
- if (mi != bitdb.mapFileUseCount.end())
+ std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
+ if (mi != env->mapFileUseCount.end())
{
LogPrint(BCLog::DB, "Flushing %s\n", strFile);
int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained
- bitdb.CloseDb(strFile);
- bitdb.CheckpointLSN(strFile);
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
- bitdb.mapFileUseCount.erase(mi++);
+ env->mapFileUseCount.erase(mi++);
LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
ret = true;
}
@@ -633,3 +637,52 @@ bool CDB::PeriodicFlush(std::string strFile)
return ret;
}
+
+bool CWalletDBWrapper::Rewrite(const char* pszSkip)
+{
+ return CDB::Rewrite(*this, pszSkip);
+}
+
+bool CWalletDBWrapper::Backup(const std::string& strDest)
+{
+ if (IsDummy()) {
+ return false;
+ }
+ while (true)
+ {
+ {
+ LOCK(env->cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
+ {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ // Copy wallet file
+ fs::path pathSrc = GetDataDir() / strFile;
+ fs::path pathDest(strDest);
+ if (fs::is_directory(pathDest))
+ pathDest /= strFile;
+
+ try {
+ fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
+ LogPrintf("copied %s to %s\n", strFile, pathDest.string());
+ return true;
+ } catch (const fs::filesystem_error& e) {
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
+ return false;
+ }
+ }
+ }
+ MilliSleep(100);
+ }
+ return false;
+}
+
+void CWalletDBWrapper::Flush(bool shutdown)
+{
+ if (!IsDummy()) {
+ env->Flush(shutdown);
+ }
+}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 9f912f9a1a..3c6870d169 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -72,7 +72,6 @@ public:
void CheckpointLSN(const std::string& strFile);
void CloseDb(const std::string& strFile);
- bool RemoveDb(const std::string& strFile);
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
{
@@ -86,6 +85,52 @@ public:
extern CDBEnv bitdb;
+/** An instance of this class represents one database.
+ * For BerkeleyDB this is just a (env, strFile) tuple.
+ **/
+class CWalletDBWrapper
+{
+ friend class CDB;
+public:
+ /** Create dummy DB handle */
+ CWalletDBWrapper(): env(nullptr)
+ {
+ }
+
+ /** Create DB handle to real database */
+ CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in):
+ env(env_in), strFile(strFile_in)
+ {
+ }
+
+ /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
+ */
+ bool Rewrite(const char* pszSkip=nullptr);
+
+ /** Back up the entire database to a file.
+ */
+ bool Backup(const std::string& strDest);
+
+ /** Get a name for this database, for debugging etc.
+ */
+ std::string GetName() const { return strFile; }
+
+ /** Make sure all changes are flushed to disk.
+ */
+ void Flush(bool shutdown);
+
+private:
+ /** BerkeleyDB specific */
+ CDBEnv *env;
+ std::string strFile;
+
+ /** Return whether this database handle is a dummy for testing.
+ * Only to be used at a low level, application should ideally not care
+ * about this.
+ */
+ bool IsDummy() { return env == nullptr; }
+};
+
/** RAII class that provides access to a Berkeley database */
class CDB
@@ -96,18 +141,19 @@ protected:
DbTxn* activeTxn;
bool fReadOnly;
bool fFlushOnClose;
+ CDBEnv *env;
- explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
+public:
+ explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
-public:
void Flush();
void Close();
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
/* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */
- static bool PeriodicFlush(std::string strFile);
+ static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* verifies the database environment */
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */
@@ -117,7 +163,7 @@ private:
CDB(const CDB&);
void operator=(const CDB&);
-protected:
+public:
template <typename K, typename T>
bool Read(const K& key, T& value)
{
@@ -134,29 +180,30 @@ protected:
Dbt datValue;
datValue.set_flags(DB_DBT_MALLOC);
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
- memset(datKey.get_data(), 0, datKey.get_size());
- if (datValue.get_data() == NULL)
- return false;
-
- // Unserialize value
- try {
- CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
- ssValue >> value;
- } catch (const std::exception&) {
- return false;
+ memory_cleanse(datKey.get_data(), datKey.get_size());
+ bool success = false;
+ if (datValue.get_data() != NULL) {
+ // Unserialize value
+ try {
+ CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
+ ssValue >> value;
+ success = true;
+ } catch (const std::exception&) {
+ // In this case success remains 'false'
+ }
+
+ // Clear and free memory
+ memory_cleanse(datValue.get_data(), datValue.get_size());
+ free(datValue.get_data());
}
-
- // Clear and free memory
- memset(datValue.get_data(), 0, datValue.get_size());
- free(datValue.get_data());
- return (ret == 0);
+ return ret == 0 && success;
}
template <typename K, typename T>
bool Write(const K& key, const T& value, bool fOverwrite = true)
{
if (!pdb)
- return false;
+ return true;
if (fReadOnly)
assert(!"Write called on database in read-only mode");
@@ -176,8 +223,8 @@ protected:
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
// Clear memory in case it was a private key
- memset(datKey.get_data(), 0, datKey.get_size());
- memset(datValue.get_data(), 0, datValue.get_size());
+ memory_cleanse(datKey.get_data(), datKey.get_size());
+ memory_cleanse(datValue.get_data(), datValue.get_size());
return (ret == 0);
}
@@ -199,7 +246,7 @@ protected:
int ret = pdb->del(activeTxn, &datKey, 0);
// Clear memory
- memset(datKey.get_data(), 0, datKey.get_size());
+ memory_cleanse(datKey.get_data(), datKey.get_size());
return (ret == 0 || ret == DB_NOTFOUND);
}
@@ -219,7 +266,7 @@ protected:
int ret = pdb->exists(activeTxn, &datKey, 0);
// Clear memory
- memset(datKey.get_data(), 0, datKey.get_size());
+ memory_cleanse(datKey.get_data(), datKey.get_size());
return (ret == 0);
}
@@ -262,8 +309,8 @@ protected:
ssValue.write((char*)datValue.get_data(), datValue.get_size());
// Clear and free memory
- memset(datKey.get_data(), 0, datKey.get_size());
- memset(datValue.get_data(), 0, datValue.get_size());
+ memory_cleanse(datKey.get_data(), datKey.get_size());
+ memory_cleanse(datValue.get_data(), datValue.get_size());
free(datKey.get_data());
free(datValue.get_data());
return 0;
@@ -310,7 +357,7 @@ public:
return Write(std::string("version"), nVersion);
}
- bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
+ bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = NULL);
};
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index fe3871a91d..b3cb6a718c 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -5,6 +5,7 @@
#include "consensus/validation.h"
#include "wallet/feebumper.h"
#include "wallet/wallet.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "policy/rbf.h"
#include "validation.h" //for mempool access
@@ -23,14 +24,14 @@
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet)
{
CMutableTransaction txNew(tx);
- std::vector<std::pair<const CWalletTx *, unsigned int>> vCoins;
+ std::vector<CInputCoin> vCoins;
// Look up the inputs. We should have already checked that this transaction
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
// wallet, with a valid index into the vout array.
for (auto& input : tx.vin) {
const auto mi = pWallet->mapWallet.find(input.prevout.hash);
assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
- vCoins.emplace_back(&(mi->second), input.prevout.n);
+ vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n));
}
if (!pWallet->DummySignTx(txNew, vCoins)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
@@ -159,11 +160,11 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
} else {
// if user specified a confirm target then don't consider any global payTxFee
if (specifiedConfirmTarget) {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, true);
}
// otherwise use the regular wallet logic to select payTxFee or default confirm target
else {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator);
}
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
@@ -213,7 +214,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
- if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
+ if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) {
LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
nNewFee += poutput->nValue;
mtx.vout.erase(mtx.vout.begin() + nOutput);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7ff9e7ae58..82708dab26 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -95,6 +95,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"") +
"\nImport using a label and without rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
+ "\nImport using default blank label and without rescan\n"
+ + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
);
@@ -154,6 +156,31 @@ UniValue importprivkey(const JSONRPCRequest& request)
return NullUniValue;
}
+UniValue abortrescan(const JSONRPCRequest& request)
+{
+ CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() > 0)
+ throw std::runtime_error(
+ "abortrescan\n"
+ "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
+ "\nExamples:\n"
+ "\nImport a private key\n"
+ + HelpExampleCli("importprivkey", "\"mykey\"") +
+ "\nAbort the running wallet rescan\n"
+ + HelpExampleCli("abortrescan", "") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("abortrescan", "")
+ );
+
+ if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
+ pwallet->AbortRescan();
+ return true;
+}
+
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
{
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2cc3072c16..fa67964ce8 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -9,8 +9,11 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
+#include "wallet/coincontrol.h"
#include "validation.h"
#include "net.h"
+#include "policy/feerate.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "policy/rbf.h"
#include "rpc/server.h"
@@ -727,6 +730,9 @@ UniValue getbalance(const JSONRPCRequest& request)
if (request.params.size() == 0)
return ValueFromAmount(pwallet->GetBalance());
+ const std::string& account_param = request.params[0].get_str();
+ const std::string* account = account_param != "*" ? &account_param : nullptr;
+
int nMinDepth = 1;
if (request.params.size() > 1)
nMinDepth = request.params[1].get_int();
@@ -735,41 +741,7 @@ UniValue getbalance(const JSONRPCRequest& request)
if(request.params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
- if (request.params[0].get_str() == "*") {
- // Calculate total balance in a very different way from GetBalance().
- // The biggest difference is that GetBalance() sums up all unspent
- // TxOuts paying to the wallet, while this sums up both spent and
- // unspent TxOuts paying to the wallet, and then subtracts the values of
- // TxIns spending from the wallet. This also has fewer restrictions on
- // which unconfirmed transactions are considered trusted.
- CAmount nBalance = 0;
- for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
- const CWalletTx& wtx = pairWtx.second;
- if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
- continue;
-
- CAmount allFee;
- std::string strSentAccount;
- std::list<COutputEntry> listReceived;
- std::list<COutputEntry> listSent;
- wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
- if (wtx.GetDepthInMainChain() >= nMinDepth)
- {
- BOOST_FOREACH(const COutputEntry& r, listReceived)
- nBalance += r.amount;
- }
- BOOST_FOREACH(const COutputEntry& s, listSent)
- nBalance -= s.amount;
- nBalance -= allFee;
- }
- return ValueFromAmount(nBalance);
- }
-
- std::string strAccount = AccountFromValue(request.params[0]);
-
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, filter);
-
- return ValueFromAmount(nBalance);
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
}
UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -899,7 +871,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
// Check funds
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -1008,7 +980,7 @@ UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
// Check funds
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -2076,7 +2048,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
int64_t nSleepTime = request.params[1].get_int64();
pwallet->nRelockTime = GetTime() + nSleepTime;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->strWalletFile), boost::bind(LockWallet, pwallet), nSleepTime);
+ RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), boost::bind(LockWallet, pwallet), nSleepTime);
return NullUniValue;
}
@@ -2914,6 +2886,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
return result;
}
+extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
extern UniValue importaddress(const JSONRPCRequest& request);
@@ -2930,6 +2903,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, true, {} },
{ "wallet", "abandontransaction", &abandontransaction, false, {"txid"} },
+ { "wallet", "abortrescan", &abortrescan, false, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} },
{ "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} },
{ "wallet", "backupwallet", &backupwallet, true, {"destination"} },
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index a76db37617..1989bf8d9b 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -14,7 +14,8 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
bitdb.MakeMock();
bool fFirstRun;
- pwalletMain = new CWallet("wallet_test.dat");
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
+ pwalletMain = new CWallet(std::move(dbw));
pwalletMain->LoadWallet(fFirstRun);
RegisterValidationInterface(pwalletMain);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 67e5e90224..8eeba72a06 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -29,7 +29,7 @@ extern UniValue importmulti(const JSONRPCRequest& request);
std::vector<std::unique_ptr<CWalletTx>> wtxn;
-typedef std::set<std::pair<const CWalletTx*,unsigned int> > CoinSet;
+typedef std::set<CInputCoin> CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
@@ -424,6 +424,17 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax()));
::pwalletMain = backup;
}
+
+ // Verify ScanForWalletTransactions does not return null when the scan is
+ // elided due to the nTimeFirstKey optimization.
+ {
+ CWallet wallet;
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1);
+ }
+ BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip));
+ }
}
// Check that GetImmatureCredit() returns a newly calculated value instead of
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d449f83a87..ea329d6ebe 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -16,6 +16,7 @@
#include "keystore.h"
#include "validation.h"
#include "net.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "policy/rbf.h"
#include "primitives/block.h"
@@ -65,10 +66,10 @@ const uint256 CMerkleTx::ABANDON_HASH(uint256S("00000000000000000000000000000000
struct CompareValueOnly
{
- bool operator()(const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t1,
- const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t2) const
+ bool operator()(const CInputCoin& t1,
+ const CInputCoin& t2) const
{
- return t1.first < t2.first;
+ return t1.txout.nValue < t2.txout.nValue;
}
};
@@ -161,7 +162,7 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter
secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database
- if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
+ if (!CWalletDB(*dbw).WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
}
@@ -180,10 +181,8 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
if (HaveWatchOnly(script))
RemoveWatchOnly(script);
- if (!fFileBacked)
- return true;
if (!IsCrypted()) {
- return CWalletDB(strWalletFile).WriteKey(pubkey,
+ return CWalletDB(*dbw).WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
@@ -195,8 +194,6 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
- if (!fFileBacked)
- return true;
{
LOCK(cs_wallet);
if (pwalletdbEncryption)
@@ -204,7 +201,7 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
else
- return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,
+ return CWalletDB(*dbw).WriteCryptedKey(vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
}
@@ -240,9 +237,7 @@ bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
+ return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript);
}
bool CWallet::LoadCScript(const CScript& redeemScript)
@@ -268,9 +263,7 @@ bool CWallet::AddWatchOnly(const CScript& dest)
const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta);
+ return CWalletDB(*dbw).WriteWatchOnly(dest, meta);
}
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
@@ -286,9 +279,8 @@ bool CWallet::RemoveWatchOnly(const CScript &dest)
return false;
if (!HaveWatchOnly())
NotifyWatchonlyChanged(false);
- if (fFileBacked)
- if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
- return false;
+ if (!CWalletDB(*dbw).EraseWatchOnly(dest))
+ return false;
return true;
}
@@ -353,7 +345,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey))
return false;
- CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
+ CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked)
Lock();
return true;
@@ -366,7 +358,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
void CWallet::SetBestChain(const CBlockLocator& loc)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
walletdb.WriteBestBlock(loc);
}
@@ -385,9 +377,8 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn,
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
- if (fFileBacked)
{
- CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
+ CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw);
if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn)
@@ -441,7 +432,7 @@ bool CWallet::HasWalletSpend(const uint256& txid) const
void CWallet::Flush(bool shutdown)
{
- bitdb.Flush(shutdown);
+ dbw->Flush(shutdown);
}
bool CWallet::Verify()
@@ -594,24 +585,19 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
- if (fFileBacked)
- {
- assert(!pwalletdbEncryption);
- pwalletdbEncryption = new CWalletDB(strWalletFile);
- if (!pwalletdbEncryption->TxnBegin()) {
- delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
- return false;
- }
- pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
+ assert(!pwalletdbEncryption);
+ pwalletdbEncryption = new CWalletDB(*dbw);
+ if (!pwalletdbEncryption->TxnBegin()) {
+ delete pwalletdbEncryption;
+ pwalletdbEncryption = NULL;
+ return false;
}
+ pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (!EncryptKeys(_vMasterKey))
{
- if (fFileBacked) {
- pwalletdbEncryption->TxnAbort();
- delete pwalletdbEncryption;
- }
+ pwalletdbEncryption->TxnAbort();
+ delete pwalletdbEncryption;
// We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload the unencrypted wallet.
assert(false);
@@ -620,30 +606,24 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
- if (fFileBacked)
- {
- if (!pwalletdbEncryption->TxnCommit()) {
- delete pwalletdbEncryption;
- // We now have keys encrypted in memory, but not on disk...
- // die to avoid confusion and let the user reload the unencrypted wallet.
- assert(false);
- }
-
+ if (!pwalletdbEncryption->TxnCommit()) {
delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
+ // We now have keys encrypted in memory, but not on disk...
+ // die to avoid confusion and let the user reload the unencrypted wallet.
+ assert(false);
}
+ delete pwalletdbEncryption;
+ pwalletdbEncryption = NULL;
+
Lock();
Unlock(strWalletPassphrase);
// if we are using HD, replace the HD master key (seed) with a new one
if (IsHDEnabled()) {
- CKey key;
- CPubKey masterPubKey = GenerateNewHDMasterKey();
- // preserve the old chains version to not break backward compatibility
- CHDChain oldChain = GetHDChain();
- if (!SetHDMasterKey(masterPubKey, &oldChain))
+ if (!SetHDMasterKey(GenerateNewHDMasterKey())) {
return false;
+ }
}
NewKeyPool();
@@ -651,7 +631,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
- CDB::Rewrite(strWalletFile);
+ dbw->Rewrite();
}
NotifyStatusChanged(this);
@@ -662,7 +642,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
DBErrors CWallet::ReorderTransactions()
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// Old wallets didn't have any defined order for transactions
// Probably a bad idea to change the output of this
@@ -743,14 +723,14 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else {
- CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
+ CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext);
}
return nRet;
}
bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
if (!walletdb.TxnBegin())
return false;
@@ -784,7 +764,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
CAccount account;
walletdb.ReadAccount(strAccount, account);
@@ -845,7 +825,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
- CWalletDB walletdb(strWalletFile, "r+");
+ CWalletDB walletdb(*dbw, "r+");
bool success = true;
if (!walletdb.WriteTx(wtx)) {
@@ -862,7 +842,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose);
+ CWalletDB walletdb(*dbw, "r+", fFlushOnClose);
uint256 hash = wtxIn.GetHash();
@@ -957,9 +937,9 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
* be set when the transaction was known to be included in a block. When
- * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not
- * updated in AddToWallet, but notifications happen and cached balances are
- * marked dirty.
+ * pIndex == NULL, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
* If fUpdate is true, existing transactions will be updated.
* TODO: One exception to this is that the abandoned state is cleared under the
* assumption that any further notification of a transaction that was considered
@@ -967,12 +947,13 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
+ const CTransaction& tx = *ptx;
{
AssertLockHeld(cs_wallet);
- if (posInBlock != -1) {
+ if (pIndex != NULL) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
@@ -989,10 +970,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
- CWalletTx wtx(this, MakeTransactionRef(tx));
+ CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
- if (posInBlock != -1)
+ if (pIndex != NULL)
wtx.SetMerkleBranch(pIndex, posInBlock);
return AddToWallet(wtx, false);
@@ -1005,7 +986,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
- CWalletDB walletdb(strWalletFile, "r+");
+ CWalletDB walletdb(*dbw, "r+");
std::set<uint256> todo;
std::set<uint256> done;
@@ -1077,7 +1058,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
return;
// Do not flush the wallet here for performance reasons
- CWalletDB walletdb(strWalletFile, "r+", false);
+ CWalletDB walletdb(*dbw, "r+", false);
std::set<uint256> todo;
std::set<uint256> done;
@@ -1117,11 +1098,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)
-{
- LOCK2(cs_main, cs_wallet);
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
+ const CTransaction& tx = *ptx;
- if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1134,6 +1114,38 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex,
}
}
+void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
+ LOCK2(cs_main, cs_wallet);
+ SyncTransaction(ptx);
+}
+
+void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+ LOCK2(cs_main, cs_wallet);
+ // TODO: Temporarily ensure that mempool removals are notified before
+ // connected transactions. This shouldn't matter, but the abandoned
+ // state of transactions in our wallet is currently cleared when we
+ // receive another notification and there is a race condition where
+ // notification of a connected conflict might cause an outside process
+ // to abandon a transaction and then have it inadvertently cleared by
+ // the notification that the conflicted transaction was evicted.
+
+ for (const CTransactionRef& ptx : vtxConflicted) {
+ SyncTransaction(ptx);
+ }
+ for (size_t i = 0; i < pblock->vtx.size(); i++) {
+ SyncTransaction(pblock->vtx[i], pindex, i);
+ }
+}
+
+void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+ LOCK2(cs_main, cs_wallet);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ SyncTransaction(ptx);
+ }
+}
+
+
isminetype CWallet::IsMine(const CTxIn &txin) const
{
@@ -1309,17 +1321,14 @@ CPubKey CWallet::GenerateNewHDMasterKey()
return pubkey;
}
-bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain)
+bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
{
LOCK(cs_wallet);
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain;
- if (possibleOldChain) {
- // preserve the old chains version
- newHdChain.nVersion = possibleOldChain->nVersion;
- }
+ newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
newHdChain.masterKeyID = pubkey.GetID();
SetHDChain(newHdChain, false);
@@ -1329,7 +1338,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain)
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
- if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
+ if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain))
throw std::runtime_error(std::string(__func__) + ": writing chain failed");
hdChain = chain;
@@ -1442,59 +1451,27 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
}
-void CWalletTx::GetAccountAmounts(const std::string& strAccount, CAmount& nReceived,
- CAmount& nSent, CAmount& nFee, const isminefilter& filter) const
-{
- nReceived = nSent = nFee = 0;
-
- CAmount allFee;
- std::string strSentAccount;
- std::list<COutputEntry> listReceived;
- std::list<COutputEntry> listSent;
- GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
-
- if (strAccount == strSentAccount)
- {
- BOOST_FOREACH(const COutputEntry& s, listSent)
- nSent += s.amount;
- nFee = allFee;
- }
- {
- LOCK(pwallet->cs_wallet);
- BOOST_FOREACH(const COutputEntry& r, listReceived)
- {
- if (pwallet->mapAddressBook.count(r.destination))
- {
- std::map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination);
- if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount)
- nReceived += r.amount;
- }
- else if (strAccount.empty())
- {
- nReceived += r.amount;
- }
- }
- }
-}
-
/**
* Scan the block chain (starting in pindexStart) for transactions
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
* Returns pointer to the first block in the last contiguous range that was
- * successfully scanned.
- *
+ * successfully scanned or elided (elided if pIndexStart points at a block
+ * before CWallet::nTimeFirstKey). Returns null if there is no such range, or
+ * the range doesn't include chainActive.Tip().
*/
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
- CBlockIndex* ret = nullptr;
int64_t nNow = GetTime();
const CChainParams& chainParams = Params();
CBlockIndex* pindex = pindexStart;
+ CBlockIndex* ret = pindexStart;
{
LOCK2(cs_main, cs_wallet);
+ fAbortRescan = false;
+ fScanningWallet = true;
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
@@ -1504,15 +1481,19 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
- while (pindex)
+ while (pindex && !fAbortRescan)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ if (GetTime() >= nNow + 60) {
+ nNow = GetTime();
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
+ }
CBlock block;
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
if (!ret) {
ret = pindex;
@@ -1521,12 +1502,13 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
ret = nullptr;
}
pindex = chainActive.Next(pindex);
- if (GetTime() >= nNow + 60) {
- nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
- }
+ }
+ if (pindex && fAbortRescan) {
+ LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
+
+ fScanningWallet = false;
}
return ret;
}
@@ -1761,10 +1743,7 @@ CAmount CWalletTx::GetChange() const
bool CWalletTx::InMempool() const
{
LOCK(mempool.cs);
- if (mempool.exists(GetHash())) {
- return true;
- }
- return false;
+ return mempool.exists(GetHash());
}
bool CWalletTx::IsTrusted() const
@@ -1955,6 +1934,49 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
return nTotal;
}
+// Calculate total balance in a different way from GetBalance. The biggest
+// difference is that GetBalance sums up all unspent TxOuts paying to the
+// wallet, while this sums up both spent and unspent TxOuts paying to the
+// wallet, and then subtracts the values of TxIns spending from the wallet. This
+// also has fewer restrictions on which unconfirmed transactions are considered
+// trusted.
+CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const
+{
+ LOCK2(cs_main, cs_wallet);
+
+ CAmount balance = 0;
+ for (const auto& entry : mapWallet) {
+ const CWalletTx& wtx = entry.second;
+ const int depth = wtx.GetDepthInMainChain();
+ if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.GetBlocksToMaturity() > 0) {
+ continue;
+ }
+
+ // Loop through tx outputs and add incoming payments. For outgoing txs,
+ // treat change outputs specially, as part of the amount debited.
+ CAmount debit = wtx.GetDebit(filter);
+ const bool outgoing = debit > 0;
+ for (const CTxOut& out : wtx.tx->vout) {
+ if (outgoing && IsChange(out)) {
+ debit -= out.nValue;
+ } else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetAccountName(out.scriptPubKey))) {
+ balance += out.nValue;
+ }
+ }
+
+ // For outgoing txs, subtract amount debited.
+ if (outgoing && (!account || *account == wtx.strFromAccount)) {
+ balance -= debit;
+ }
+ }
+
+ if (account) {
+ balance += CWalletDB(*dbw).GetAccountCreditDebit(*account);
+ }
+
+ return balance;
+}
+
void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
{
vCoins.clear();
@@ -2032,7 +2054,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
}
-static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
+static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
std::vector<char> vfIncluded;
@@ -2057,9 +2079,9 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
//that the rng is fast. We do not use a constant random sequence,
//because there may be some privacy improvement by making
//the selection random.
- if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
+ if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
{
- nTotal += vValue[i].first;
+ nTotal += vValue[i].txout.nValue;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
@@ -2069,7 +2091,7 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
nBest = nTotal;
vfBest = vfIncluded;
}
- nTotal -= vValue[i].first;
+ nTotal -= vValue[i].txout.nValue;
vfIncluded[i] = false;
}
}
@@ -2079,16 +2101,14 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
}
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins,
- std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
- std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = std::numeric_limits<CAmount>::max();
- coinLowestLarger.second.first = NULL;
- std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > > vValue;
+ boost::optional<CInputCoin> coinLowestLarger;
+ std::vector<CInputCoin> vValue;
CAmount nTotalLower = 0;
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
@@ -2107,22 +2127,21 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
continue;
int i = output.i;
- CAmount n = pcoin->tx->vout[i].nValue;
- std::pair<CAmount,std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n,std::make_pair(pcoin, i));
+ CInputCoin coin = CInputCoin(pcoin, i);
- if (n == nTargetValue)
+ if (coin.txout.nValue == nTargetValue)
{
- setCoinsRet.insert(coin.second);
- nValueRet += coin.first;
+ setCoinsRet.insert(coin);
+ nValueRet += coin.txout.nValue;
return true;
}
- else if (n < nTargetValue + MIN_CHANGE)
+ else if (coin.txout.nValue < nTargetValue + MIN_CHANGE)
{
vValue.push_back(coin);
- nTotalLower += n;
+ nTotalLower += coin.txout.nValue;
}
- else if (n < coinLowestLarger.first)
+ else if (!coinLowestLarger || coin.txout.nValue < coinLowestLarger->txout.nValue)
{
coinLowestLarger = coin;
}
@@ -2132,18 +2151,18 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
{
for (unsigned int i = 0; i < vValue.size(); ++i)
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
return true;
}
if (nTotalLower < nTargetValue)
{
- if (coinLowestLarger.second.first == NULL)
+ if (!coinLowestLarger)
return false;
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
return true;
}
@@ -2159,25 +2178,25 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
- if (coinLowestLarger.second.first &&
- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))
+ if (coinLowestLarger &&
+ ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger->txout.nValue <= nBest))
{
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
}
else {
for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].first));
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue));
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
@@ -2187,7 +2206,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
return true;
}
-bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
+bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
{
std::vector<COutput> vCoins(vAvailableCoins);
@@ -2199,13 +2218,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (!out.fSpendable)
continue;
nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(std::make_pair(out.tx, out.i));
+ setCoinsRet.insert(CInputCoin(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
}
// calculate value from preset inputs and store them
- std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins;
+ std::set<CInputCoin> setPresetCoins;
CAmount nValueFromPresetInputs = 0;
std::vector<COutPoint> vPresetInputs;
@@ -2221,7 +2240,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (pcoin->tx->vout.size() <= outpoint.n)
return false;
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(std::make_pair(pcoin, outpoint.n));
+ setPresetCoins.insert(CInputCoin(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
}
@@ -2229,7 +2248,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
{
- if (setPresetCoins.count(std::make_pair(it->tx, it->i)))
+ if (setPresetCoins.count(CInputCoin(it->tx, it->i)))
it = vCoins.erase(it);
else
++it;
@@ -2395,7 +2414,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
- std::set<std::pair<const CWalletTx*,unsigned int> > setCoins;
+ std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2430,7 +2449,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
}
}
- if (txout.IsDust(dustRelayFee))
+ if (IsDust(txout, ::dustRelayFee))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -2495,16 +2514,16 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// We do not move dust-change to fees, because the sender would end up paying more than requested.
// This would be against the purpose of the all-inclusive feature.
// So instead we raise the change and deduct from the recipient.
- if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
+ if (nSubtractFeeFromAmount > 0 && IsDust(newTxOut, ::dustRelayFee))
{
- CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
+ CAmount nDust = GetDustThreshold(newTxOut, ::dustRelayFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
- if (txNew.vout[i].IsDust(dustRelayFee))
+ if (IsDust(txNew.vout[i], ::dustRelayFee))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
return false;
@@ -2516,7 +2535,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (newTxOut.IsDust(dustRelayFee))
+ if (IsDust(newTxOut, ::dustRelayFee))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2538,9 +2557,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
txNew.vout.insert(position, newTxOut);
}
- }
- else
+ } else {
reservekey.ReturnKey();
+ nChangePosInOut = -1;
+ }
// Fill vin
//
@@ -2554,7 +2574,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// behavior."
bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf;
for (const auto& coin : setCoins)
- txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
+ txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
std::numeric_limits<unsigned int>::max() - (rbf ? 2 : 1)));
// Fill in dummy signatures for fee calculation.
@@ -2578,7 +2598,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget;
- CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool);
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator);
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
@@ -2637,10 +2657,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
int nIn = 0;
for (const auto& coin : setCoins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
- if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
@@ -2724,13 +2744,13 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
}
void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) {
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
return walletdb.ListAccountCreditDebit(strAccount, entries);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
return AddAccountingEntry(acentry, &walletdb);
}
@@ -2752,19 +2772,14 @@ 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)
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee)
{
// payTxFee is the user-set global for desired feerate
- return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
-}
-
-CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
-{
- CAmount nFeeNeeded = targetFee;
+ CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
// User didn't set: use -txconfirmtarget to estimate...
- if (nFeeNeeded == 0) {
+ if (nFeeNeeded == 0 || ignoreUserSetFee) {
int estimateFoundTarget = nConfirmTarget;
- nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
+ nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool).GetFee(nTxBytes);
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
if (nFeeNeeded == 0)
nFeeNeeded = fallbackFee.GetFee(nTxBytes);
@@ -2782,13 +2797,11 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
fFirstRunRet = false;
- DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+ DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
@@ -2809,17 +2822,15 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
AssertLockHeld(cs_wallet); // mapWallet
vchDefaultKey = CPubKey();
- DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(vHashIn, vHashOut);
+ DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut)
mapWallet.erase(hash);
if (nZapSelectTxRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
setKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked.
@@ -2839,13 +2850,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
vchDefaultKey = CPubKey();
- DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(vWtx);
+ DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
@@ -2875,11 +2884,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
- if (!fFileBacked)
+ if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
return false;
- if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
- return false;
- return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
+ return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
@@ -2887,33 +2894,40 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
{
LOCK(cs_wallet); // mapAddressBook
- if(fFileBacked)
+ // Delete destdata tuples associated with address
+ std::string strAddress = CBitcoinAddress(address).ToString();
+ BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)
{
- // Delete destdata tuples associated with address
- std::string strAddress = CBitcoinAddress(address).ToString();
- BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)
- {
- CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);
- }
+ CWalletDB(*dbw).EraseDestData(strAddress, item.first);
}
mapAddressBook.erase(address);
}
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
- if (!fFileBacked)
- return false;
- CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());
- return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
+ CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString());
+ return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString());
}
-bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
+const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
{
- if (fFileBacked)
- {
- if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))
- return false;
+ CTxDestination address;
+ if (ExtractDestination(scriptPubKey, address) && !scriptPubKey.IsUnspendable()) {
+ auto mi = mapAddressBook.find(address);
+ if (mi != mapAddressBook.end()) {
+ return mi->second.name;
+ }
}
+ // A scriptPubKey that doesn't have an entry in the address book is
+ // associated with the default account ("").
+ const static std::string DEFAULT_ACCOUNT_NAME;
+ return DEFAULT_ACCOUNT_NAME;
+}
+
+bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
+{
+ if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
+ return false;
vchDefaultKey = vchPubKey;
return true;
}
@@ -2926,7 +2940,7 @@ bool CWallet::NewKeyPool()
{
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
BOOST_FOREACH(int64_t nIndex, setKeyPool)
walletdb.ErasePool(nIndex);
setKeyPool.clear();
@@ -2947,7 +2961,7 @@ size_t CWallet::KeypoolCountExternalKeys()
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
return setKeyPool.size();
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// count amount of external keys
size_t amountE = 0;
@@ -2990,7 +3004,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
missingInternal = 0;
}
bool internal = false;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;)
{
int64_t nEnd = 1;
@@ -3021,7 +3035,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
if(setKeyPool.empty())
return;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// try to find a key that matches the internal/external filter
for(const int64_t& id : setKeyPool)
@@ -3047,11 +3061,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
void CWallet::KeepKey(int64_t nIndex)
{
// Remove from key pool
- if (fFileBacked)
- {
- CWalletDB walletdb(strWalletFile);
- walletdb.ErasePool(nIndex);
- }
+ CWalletDB walletdb(*dbw);
+ walletdb.ErasePool(nIndex);
LogPrintf("keypool keep %d\n", nIndex);
}
@@ -3093,7 +3104,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
return GetTime();
CKeyPool keypool;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT))
{
@@ -3259,37 +3270,6 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
return ret;
}
-CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter)
-{
- CWalletDB walletdb(strWalletFile);
- return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
-}
-
-CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter)
-{
- CAmount nBalance = 0;
-
- // Tally wallet transactions
- for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
- {
- const CWalletTx& wtx = (*it).second;
- if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
- continue;
-
- CAmount nReceived, nSent, nFee;
- wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
-
- if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
- nBalance += nReceived;
- nBalance -= nSent + nFee;
- }
-
- // Tally internal accounting entries
- nBalance += walletdb.GetAccountCreditDebit(strAccount);
-
- return nBalance;
-}
-
std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const
{
LOCK(cs_wallet);
@@ -3341,7 +3321,7 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
{
setAddress.clear();
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(const int64_t& id, setKeyPool)
@@ -3357,20 +3337,9 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
}
}
-void CWallet::UpdatedTransaction(const uint256 &hashTx)
+void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
{
- {
- LOCK(cs_wallet);
- // Only notify UI if this transaction is in this wallet
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
- if (mi != mapWallet.end())
- NotifyTransactionChanged(this, hashTx, CT_UPDATED);
- }
-}
-
-void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
-{
- boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this));
+ std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
CPubKey pubkey;
if (!rKey->GetReservedKey(pubkey))
return;
@@ -3574,18 +3543,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
+ return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{
if (!mapAddressBook[dest].destdata.erase(key))
return false;
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);
+ return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key);
}
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@@ -3655,7 +3620,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
- CWallet *tempWallet = new CWallet(walletFile);
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
+ CWallet *tempWallet = new CWallet(std::move(dbw));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3670,7 +3636,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
- CWallet *walletInstance = new CWallet(walletFile);
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
+ CWallet *walletInstance = new CWallet(std::move(dbw));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
@@ -3761,7 +3728,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!GetBoolArg("-rescan", false))
{
- CWalletDB walletdb(walletFile);
+ CWalletDB walletdb(*walletInstance->dbw);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = FindForkInGlobalIndex(chainActive, locator);
@@ -3794,7 +3761,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
// Restore wallet transaction metadata after -zapwallettxes=1
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
{
- CWalletDB walletdb(walletFile);
+ CWalletDB walletdb(*walletInstance->dbw);
BOOST_FOREACH(const CWalletTx& wtxOld, vWtx)
{
@@ -3838,7 +3805,7 @@ bool CWallet::InitLoadWallet()
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
- if (walletFile.find_first_of("/\\") != std::string::npos) {
+ if (boost::filesystem::path(walletFile).filename() != walletFile) {
return InitError(_("-wallet parameter must only specify a filename (not a path)"));
} else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
return InitError(_("Invalid characters in -wallet filename"));
@@ -3954,38 +3921,7 @@ bool CWallet::ParameterInteraction()
bool CWallet::BackupWallet(const std::string& strDest)
{
- if (!fFileBacked)
- return false;
- while (true)
- {
- {
- LOCK(bitdb.cs_db);
- if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0)
- {
- // Flush log data to the dat file
- bitdb.CloseDb(strWalletFile);
- bitdb.CheckpointLSN(strWalletFile);
- bitdb.mapFileUseCount.erase(strWalletFile);
-
- // Copy wallet file
- fs::path pathSrc = GetDataDir() / strWalletFile;
- fs::path pathDest(strDest);
- if (fs::is_directory(pathDest))
- pathDest /= strWalletFile;
-
- try {
- fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
- LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string());
- return true;
- } catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what());
- return false;
- }
- }
- }
- MilliSleep(100);
- }
- return false;
+ return dbw->Backup(strDest);
}
CKeyPool::CKeyPool()
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index c714ddd090..179ea1b7ad 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
+#include "policy/feerate.h"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
@@ -28,8 +29,6 @@
#include <utility>
#include <vector>
-#include <boost/shared_ptr.hpp>
-
extern CWallet* pwalletMain;
/**
@@ -75,6 +74,7 @@ class CReserveKey;
class CScript;
class CScheduler;
class CTxMemPool;
+class CBlockPolicyEstimator;
class CWalletTx;
/** (client) version numbers for particular wallet features */
@@ -452,9 +452,6 @@ public:
void GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
- void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived,
- CAmount& nSent, CAmount& nFee, const isminefilter& filter) const;
-
bool IsFromMe(const isminefilter& filter) const
{
return (GetDebit(filter) > 0);
@@ -475,7 +472,34 @@ public:
};
+class CInputCoin {
+public:
+ CInputCoin(const CWalletTx* walletTx, unsigned int i)
+ {
+ if (!walletTx)
+ throw std::invalid_argument("walletTx should not be null");
+ if (i >= walletTx->tx->vout.size())
+ throw std::out_of_range("The output index is out of range");
+
+ outpoint = COutPoint(walletTx->GetHash(), i);
+ txout = walletTx->tx->vout[i];
+ }
+
+ COutPoint outpoint;
+ CTxOut txout;
+
+ bool operator<(const CInputCoin& rhs) const {
+ return outpoint < rhs.outpoint;
+ }
+
+ bool operator!=(const CInputCoin& rhs) const {
+ return outpoint != rhs.outpoint;
+ }
+ bool operator==(const CInputCoin& rhs) const {
+ return outpoint == rhs.outpoint;
+ }
+};
class COutput
{
@@ -626,13 +650,15 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
{
private:
static std::atomic<bool> fFlushScheduled;
+ std::atomic<bool> fAbortRescan;
+ std::atomic<bool> fScanningWallet;
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours
*/
- bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
+ bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
CWalletDB *pwalletdbEncryption;
@@ -661,14 +687,16 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = NULL, int posInBlock = 0);
+
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false);
- bool fFileBacked;
-
std::set<int64_t> setKeyPool;
int64_t nTimeFirstKey;
@@ -684,17 +712,33 @@ private:
*/
bool AddWatchOnly(const CScript& dest) override;
+ std::unique_ptr<CWalletDBWrapper> dbw;
+
public:
/*
* Main wallet lock.
- * This lock protects all the fields added by CWallet
- * except for:
- * fFileBacked (immutable after instantiation)
- * strWalletFile (immutable after instantiation)
+ * This lock protects all the fields added by CWallet.
*/
mutable CCriticalSection cs_wallet;
- const std::string strWalletFile;
+ /** Get database handle used by this wallet. Ideally this function would
+ * not be necessary.
+ */
+ CWalletDBWrapper& GetDBHandle()
+ {
+ return *dbw;
+ }
+
+ /** Get a name for this wallet for logging/debugging purposes.
+ */
+ std::string GetName() const
+ {
+ if (dbw) {
+ return dbw->GetName();
+ } else {
+ return "dummy";
+ }
+ }
void LoadKeyPool(int nIndex, const CKeyPool &keypool)
{
@@ -716,15 +760,16 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
- CWallet()
+ // Create wallet with dummy database handle
+ CWallet(): dbw(new CWalletDBWrapper())
{
SetNull();
}
- CWallet(const std::string& strWalletFileIn) : strWalletFile(strWalletFileIn)
+ // Create wallet with passed-in database handle
+ CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
{
SetNull();
- fFileBacked = true;
}
~CWallet()
@@ -737,7 +782,6 @@ public:
{
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
- fFileBacked = false;
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
@@ -746,6 +790,8 @@ public:
nTimeFirstKey = 0;
fBroadcastTransactions = false;
nRelockTime = 0;
+ fAbortRescan = false;
+ fScanningWallet = false;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -780,7 +826,7 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -790,6 +836,13 @@ public:
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
+ /*
+ * Rescan abort properties
+ */
+ void AbortRescan() { fAbortRescan = true; }
+ bool IsAbortingRescan() { return fAbortRescan; }
+ bool IsScanning() { return fScanningWallet; }
+
/**
* keystore implementation
* Generate a new key
@@ -849,8 +902,10 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
bool LoadToWallet(const CWalletTx& wtxIn);
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override;
- bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
@@ -861,6 +916,7 @@ public:
CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
+ CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
/**
* Insert additional inputs into the transaction by
@@ -890,12 +946,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);
- /**
- * Estimate the minimum fee considering required fee and targetFee or if 0
- * then fee estimation for nConfirmTarget
- */
- static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
+ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee = false);
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
@@ -915,8 +966,6 @@ public:
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, CAmount> GetAddressBalances();
- CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter);
- CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter);
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
isminetype IsMine(const CTxIn& txin) const;
@@ -947,7 +996,7 @@ public:
bool DelAddressBook(const CTxDestination& address);
- void UpdatedTransaction(const uint256 &hashTx) override;
+ const std::string& GetAccountName(const CScript& scriptPubKey) const;
void Inventory(const uint256 &hash) override
{
@@ -959,12 +1008,7 @@ public:
}
}
- void GetScriptForMining(boost::shared_ptr<CReserveScript> &script) override;
- void ResetRequestCount(const uint256 &hash) override
- {
- LOCK(cs_wallet);
- mapRequestCount[hash] = 0;
- };
+ void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override;
unsigned int GetKeyPoolSize()
{
@@ -1057,9 +1101,10 @@ public:
CPubKey GenerateNewHDMasterKey();
/* Set the current HD master key (will reset the chain child index counters)
- If possibleOldChain is provided, the parameters from the old chain (version)
- will be preserved. */
- bool SetHDMasterKey(const CPubKey& key, CHDChain *possibleOldChain = nullptr);
+ Sets the master key's version based on the current wallet version (so the
+ caller must ensure the current wallet version is correct before calling
+ this function). */
+ bool SetHDMasterKey(const CPubKey& key);
};
/** A key allocated from the key pool. */
@@ -1132,7 +1177,7 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins
int nIn = 0;
for (const auto& coin : coins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index ceff2d36e3..a90fa6dbbd 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -33,7 +33,7 @@ static std::atomic<unsigned int> nWalletDBUpdateCounter;
bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName)
{
nWalletDBUpdateCounter++;
- return Write(make_pair(std::string("name"), strAddress), strName);
+ return batch.Write(std::make_pair(std::string("name"), strAddress), strName);
}
bool CWalletDB::EraseName(const std::string& strAddress)
@@ -41,38 +41,38 @@ bool CWalletDB::EraseName(const std::string& strAddress)
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
nWalletDBUpdateCounter++;
- return Erase(make_pair(std::string("name"), strAddress));
+ return batch.Erase(std::make_pair(std::string("name"), strAddress));
}
bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
- return Write(make_pair(std::string("purpose"), strAddress), strPurpose);
+ return batch.Write(std::make_pair(std::string("purpose"), strAddress), strPurpose);
}
bool CWalletDB::ErasePurpose(const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
- return Erase(make_pair(std::string("purpose"), strPurpose));
+ return batch.Erase(std::make_pair(std::string("purpose"), strPurpose));
}
bool CWalletDB::WriteTx(const CWalletTx& wtx)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
+ return batch.Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
}
bool CWalletDB::EraseTx(uint256 hash)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("tx"), hash));
+ return batch.Erase(std::make_pair(std::string("tx"), hash));
}
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
+ if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false))
return false;
@@ -82,7 +82,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
- return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
+ return batch.Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
@@ -92,16 +92,16 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const bool fEraseUnencryptedKey = true;
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
+ if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta))
return false;
- if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
+ if (!batch.Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
return false;
if (fEraseUnencryptedKey)
{
- Erase(std::make_pair(std::string("key"), vchPubKey));
- Erase(std::make_pair(std::string("wkey"), vchPubKey));
+ batch.Erase(std::make_pair(std::string("key"), vchPubKey));
+ batch.Erase(std::make_pair(std::string("wkey"), vchPubKey));
}
return true;
}
@@ -109,92 +109,92 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
+ return batch.Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
+ return batch.Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
+ if (!batch.Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
return false;
- return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
+ return batch.Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
}
bool CWalletDB::EraseWatchOnly(const CScript &dest)
{
nWalletDBUpdateCounter++;
- if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
+ if (!batch.Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
return false;
- return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
+ return batch.Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdateCounter++;
- Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
- return Write(std::string("bestblock_nomerkle"), locator);
+ batch.Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
+ return batch.Write(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
{
- if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
- return Read(std::string("bestblock_nomerkle"), locator);
+ if (batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
+ return batch.Read(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{
nWalletDBUpdateCounter++;
- return Write(std::string("orderposnext"), nOrderPosNext);
+ return batch.Write(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
nWalletDBUpdateCounter++;
- return Write(std::string("defaultkey"), vchPubKey);
+ return batch.Write(std::string("defaultkey"), vchPubKey);
}
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return Read(std::make_pair(std::string("pool"), nPool), keypool);
+ return batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("pool"), nPool), keypool);
+ return batch.Write(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::ErasePool(int64_t nPool)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("pool"), nPool));
+ return batch.Erase(std::make_pair(std::string("pool"), nPool));
}
bool CWalletDB::WriteMinVersion(int nVersion)
{
- return Write(std::string("minversion"), nVersion);
+ return batch.Write(std::string("minversion"), nVersion);
}
bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
{
account.SetNull();
- return Read(make_pair(std::string("acc"), strAccount), account);
+ return batch.Read(std::make_pair(std::string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account)
{
- return Write(make_pair(std::string("acc"), strAccount), account);
+ return batch.Write(std::make_pair(std::string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
{
- return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
+ return batch.Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
}
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
@@ -218,7 +218,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
{
bool fAllAccounts = (strAccount == "*");
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor");
bool setRange = true;
@@ -229,7 +229,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
if (setRange)
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0)));
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange);
setRange = false;
if (ret == DB_NOTFOUND)
break;
@@ -560,7 +560,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (Read((std::string)"minversion", nMinVersion))
+ if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
@@ -568,7 +568,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
}
// Get cursor
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
@@ -580,7 +580,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
@@ -664,14 +664,14 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
try {
int nMinVersion = 0;
- if (Read((std::string)"minversion", nMinVersion))
+ if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
}
// Get cursor
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
@@ -683,7 +683,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
@@ -797,9 +797,9 @@ void MaybeCompactWalletDB()
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{
- const std::string& strFile = pwalletMain->strWalletFile;
- if (CDB::PeriodicFlush(strFile))
+ if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) {
nLastFlushed = CWalletDB::GetUpdateCounter();
+ }
}
fOneThread = false;
}
@@ -855,20 +855,20 @@ bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
+ return batch.Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
}
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
+ return batch.Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
}
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdateCounter++;
- return Write(std::string("hdchain"), chain);
+ return batch.Write(std::string("hdchain"), chain);
}
void CWalletDB::IncrementUpdateCounter()
@@ -880,3 +880,28 @@ unsigned int CWalletDB::GetUpdateCounter()
{
return nWalletDBUpdateCounter;
}
+
+bool CWalletDB::TxnBegin()
+{
+ return batch.TxnBegin();
+}
+
+bool CWalletDB::TxnCommit()
+{
+ return batch.TxnCommit();
+}
+
+bool CWalletDB::TxnAbort()
+{
+ return batch.TxnAbort();
+}
+
+bool CWalletDB::ReadVersion(int& nVersion)
+{
+ return batch.ReadVersion(nVersion);
+}
+
+bool CWalletDB::WriteVersion(int nVersion)
+{
+ return batch.WriteVersion(nVersion);
+}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index b94f341b2e..cd9fe279c5 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -17,6 +17,21 @@
#include <utility>
#include <vector>
+/**
+ * Overview of wallet database classes:
+ *
+ * - CDBEnv is an environment in which the database exists (has no analog in dbwrapper.h)
+ * - CWalletDBWrapper represents a wallet database (similar to CDBWrapper in dbwrapper.h)
+ * - CDB is a low-level database transaction (similar to CDBBatch in dbwrapper.h)
+ * - CWalletDB is a modifier object for the wallet, and encapsulates a database
+ * transaction as well as methods to act on the database (no analog in
+ * dbwrapper.h)
+ *
+ * The latter two are named confusingly, in contrast to what the names CDB
+ * and CWalletDB suggest they are transient transaction objects and don't
+ * represent the database itself.
+ */
+
static const bool DEFAULT_FLUSHWALLET = true;
class CAccount;
@@ -118,11 +133,16 @@ public:
}
};
-/** Access to the wallet database */
-class CWalletDB : public CDB
+/** Access to the wallet database.
+ * This should really be named CWalletDBBatch, as it represents a single transaction at the
+ * database. It will be committed when the object goes out of scope.
+ * Optionally (on by default) it will flush to disk as well.
+ */
+class CWalletDB
{
public:
- CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool _fFlushOnClose = true) : CDB(strFilename, pszMode, _fFlushOnClose)
+ CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) :
+ batch(dbw, pszMode, _fFlushOnClose)
{
}
@@ -194,7 +214,20 @@ public:
static void IncrementUpdateCounter();
static unsigned int GetUpdateCounter();
+
+ //! Begin a new transaction
+ bool TxnBegin();
+ //! Commit current transaction
+ bool TxnCommit();
+ //! Abort current transaction
+ bool TxnAbort();
+ //! Read wallet version
+ bool ReadVersion(int& nVersion);
+ //! Write wallet version
+ bool WriteVersion(int nVersion);
private:
+ CDB batch;
+
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);
};
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index fac2a3c57a..c063898056 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -144,8 +144,12 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, co
}
}
-void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
+void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
{
+ // Used by BlockConnected and BlockDisconnected as well, because they're
+ // all the same external callback.
+ const CTransaction& tx = *ptx;
+
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
{
CZMQAbstractNotifier *notifier = *i;
@@ -160,3 +164,19 @@ void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CB
}
}
}
+
+void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction added in the block
+ TransactionAddedToMempool(ptx);
+ }
+}
+
+void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction removed in block disconnection
+ TransactionAddedToMempool(ptx);
+ }
+}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index beabb78da6..eec6f7bc64 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -8,6 +8,7 @@
#include "validationinterface.h"
#include <string>
#include <map>
+#include <list>
class CBlockIndex;
class CZMQAbstractNotifier;
@@ -24,8 +25,10 @@ protected:
void Shutdown();
// CValidationInterface
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
- void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
private:
CZMQNotificationInterface();
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index b2963e9bde..700c39f66e 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "chain.h"
#include "chainparams.h"
#include "streams.h"
#include "zmqpublishnotifier.h"