aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.test.include11
-rw-r--r--src/addrman.cpp99
-rw-r--r--src/addrman.h43
-rw-r--r--src/arith_uint256.h2
-rw-r--r--src/base58.cpp232
-rw-r--r--src/base58.h93
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/prevector.cpp77
-rw-r--r--src/bench/prevector_destructor.cpp36
-rw-r--r--src/bitcoin-cli.cpp6
-rw-r--r--src/bitcoin-tx.cpp10
-rw-r--r--src/chainparams.cpp8
-rw-r--r--src/chainparams.h3
-rw-r--r--src/compat.h10
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/crypto/common.h4
-rw-r--r--src/dbwrapper.cpp20
-rw-r--r--src/dbwrapper.h6
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/init.cpp7
-rw-r--r--src/key.cpp6
-rw-r--r--src/key_io.cpp227
-rw-r--r--src/key_io.h29
-rw-r--r--src/miner.cpp6
-rw-r--r--src/net.cpp21
-rw-r--r--src/net.h1
-rw-r--r--src/net_processing.cpp43
-rw-r--r--src/net_processing.h18
-rw-r--r--src/netbase.cpp2
-rw-r--r--src/prevector.h114
-rw-r--r--src/protocol.cpp13
-rw-r--r--src/protocol.h12
-rw-r--r--src/qt/addresstablemodel.cpp3
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoinaddressvalidator.cpp2
-rw-r--r--src/qt/bitcoingui.cpp1
-rw-r--r--src/qt/coincontroldialog.cpp1
-rw-r--r--src/qt/forms/modaloverlay.ui6
-rw-r--r--src/qt/forms/sendcoinsdialog.ui4
-rw-r--r--src/qt/guiutil.cpp3
-rw-r--r--src/qt/paymentrequestplus.cpp1
-rw-r--r--src/qt/paymentrequestplus.h3
-rw-r--r--src/qt/paymentserver.cpp4
-rw-r--r--src/qt/sendcoinsdialog.cpp9
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/paymentservertests.cpp1
-rw-r--r--src/qt/test/wallettests.cpp8
-rw-r--r--src/qt/transactiondesc.cpp6
-rw-r--r--src/qt/transactiondescdialog.cpp2
-rw-r--r--src/qt/transactionfilterproxy.cpp2
-rw-r--r--src/qt/transactionrecord.cpp4
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/transactiontablemodel.cpp4
-rw-r--r--src/qt/transactiontablemodel.h2
-rw-r--r--src/qt/transactionview.cpp33
-rw-r--r--src/qt/transactionview.h4
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h5
-rw-r--r--src/qt/walletview.cpp6
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp42
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/misc.cpp11
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/util.cpp2
-rw-r--r--src/script/interpreter.cpp18
-rw-r--r--src/script/sign.cpp7
-rw-r--r--src/test/addrman_tests.cpp187
-rw-r--r--src/test/base32_tests.cpp4
-rw-r--r--src/test/base58_tests.cpp140
-rw-r--r--src/test/base64_tests.cpp4
-rw-r--r--src/test/bip32_tests.cpp18
-rw-r--r--src/test/bloom_tests.cpp7
-rw-r--r--src/test/data/key_io_invalid.json (renamed from src/test/data/base58_keys_invalid.json)0
-rw-r--r--src/test/data/key_io_valid.json (renamed from src/test/data/base58_keys_valid.json)0
-rw-r--r--src/test/data/tx_valid.json4
-rw-r--r--src/test/key_io_tests.cpp149
-rw-r--r--src/test/key_tests.cpp27
-rw-r--r--src/test/net_tests.cpp2
-rw-r--r--src/test/prevector_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp2
-rw-r--r--src/test/scheduler_tests.cpp4
-rw-r--r--src/test/util_tests.cpp87
-rw-r--r--src/util.cpp17
-rw-r--r--src/util.h5
-rw-r--r--src/utilstrencodings.cpp297
-rw-r--r--src/utilstrencodings.h6
-rw-r--r--src/utiltime.cpp12
-rw-r--r--src/utiltime.h8
-rw-r--r--src/validation.cpp75
-rw-r--r--src/wallet/db.cpp120
-rw-r--r--src/wallet/db.h55
-rw-r--r--src/wallet/fees.cpp3
-rw-r--r--src/wallet/init.cpp44
-rw-r--r--src/wallet/rpcdump.cpp62
-rw-r--r--src/wallet/rpcwallet.cpp49
-rw-r--r--src/wallet/test/accounting_tests.cpp50
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp16
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp43
-rw-r--r--src/wallet/wallet.cpp19
-rw-r--r--src/wallet/wallet.h33
-rw-r--r--src/wallet/walletdb.cpp18
-rw-r--r--src/wallet/walletdb.h8
110 files changed, 1742 insertions, 1234 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ac822d6c5e..0a9370c85c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,8 +4,8 @@
DIST_SUBDIRS = secp256k1 univalue
-AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS)
-AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS)
+AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS)
+AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS)
AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
EXTRA_LIBRARIES =
@@ -105,6 +105,7 @@ BITCOIN_CORE_H = \
indirectmap.h \
init.h \
key.h \
+ key_io.h \
keystore.h \
dbwrapper.h \
limitedmap.h \
@@ -327,6 +328,7 @@ libbitcoin_common_a_SOURCES = \
core_read.cpp \
core_write.cpp \
key.cpp \
+ key_io.cpp \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 13c27299f8..748c5b7887 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -27,7 +27,7 @@ bench_bench_bitcoin_SOURCES = \
bench/lockedpool.cpp \
bench/perf.cpp \
bench/perf.h \
- bench/prevector_destructor.cpp
+ bench/prevector.cpp
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index d4d972b2bb..4ee9102519 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -9,13 +9,13 @@ TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)
JSON_TEST_FILES = \
- test/data/script_tests.json \
- test/data/base58_keys_valid.json \
test/data/base58_encode_decode.json \
- test/data/base58_keys_invalid.json \
+ test/data/key_io_valid.json \
+ test/data/key_io_invalid.json \
+ test/data/script_tests.json \
+ test/data/sighash.json \
test/data/tx_invalid.json \
- test/data/tx_valid.json \
- test/data/sighash.json
+ test/data/tx_valid.json
RAW_TEST_FILES =
@@ -45,6 +45,7 @@ BITCOIN_TESTS =\
test/DoS_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
+ test/key_io_tests.cpp \
test/key_tests.cpp \
test/limitedmap_tests.cpp \
test/dbwrapper_tests.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 9eefffb45b..e811dd4bea 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -187,7 +187,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
info.fInTried = true;
}
-void CAddrMan::Good_(const CService& addr, int64_t nTime)
+void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
int nId;
@@ -233,10 +233,22 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime)
if (nUBucket == -1)
return;
- LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
+ // which tried bucket to move the entry to
+ int tried_bucket = info.GetTriedBucket(nKey);
+ int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
+
+ // Will moving this address into tried evict another entry?
+ if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
+ LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size());
+ if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
+ m_tried_collisions.insert(nId);
+ }
+ } else {
+ LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
- // move nId to the tried tables
- MakeTried(info, nId);
+ // move nId to the tried tables
+ MakeTried(info, nId);
+ }
}
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
@@ -521,3 +533,82 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
int CAddrMan::RandomInt(int nMax){
return GetRandInt(nMax);
}
+
+void CAddrMan::ResolveCollisions_()
+{
+ for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
+ int id_new = *it;
+
+ bool erase_collision = false;
+
+ // If id_new not found in mapInfo remove it from m_tried_collisions
+ if (mapInfo.count(id_new) != 1) {
+ erase_collision = true;
+ } else {
+ CAddrInfo& info_new = mapInfo[id_new];
+
+ // Which tried bucket to move the entry to.
+ int tried_bucket = info_new.GetTriedBucket(nKey);
+ int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
+ if (!info_new.IsValid()) { // id_new may no longer map to a valid address
+ erase_collision = true;
+ } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
+
+ // Get the to-be-evicted address that is being tested
+ int id_old = vvTried[tried_bucket][tried_bucket_pos];
+ CAddrInfo& info_old = mapInfo[id_old];
+
+ // Has successfully connected in last X hours
+ if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
+ erase_collision = true;
+ } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
+
+ // Give address at least 60 seconds to successfully connect
+ if (GetAdjustedTime() - info_old.nLastTry > 60) {
+ LogPrint(BCLog::ADDRMAN, "Swapping %s for %s in tried table\n", info_new.ToString(), info_old.ToString());
+
+ // Replaces an existing address already in the tried table with the new address
+ Good_(info_new, false, GetAdjustedTime());
+ erase_collision = true;
+ }
+ }
+ } else { // Collision is not actually a collision anymore
+ Good_(info_new, false, GetAdjustedTime());
+ erase_collision = true;
+ }
+ }
+
+ if (erase_collision) {
+ m_tried_collisions.erase(it++);
+ } else {
+ it++;
+ }
+ }
+}
+
+CAddrInfo CAddrMan::SelectTriedCollision_()
+{
+ if (m_tried_collisions.size() == 0) return CAddrInfo();
+
+ std::set<int>::iterator it = m_tried_collisions.begin();
+
+ // Selects a random element from m_tried_collisions
+ std::advance(it, GetRandInt(m_tried_collisions.size()));
+ int id_new = *it;
+
+ // If id_new not found in mapInfo remove it from m_tried_collisions
+ if (mapInfo.count(id_new) != 1) {
+ m_tried_collisions.erase(it);
+ return CAddrInfo();
+ }
+
+ CAddrInfo& newInfo = mapInfo[id_new];
+
+ // which tried bucket to move the entry to
+ int tried_bucket = newInfo.GetTriedBucket(nKey);
+ int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
+
+ int id_old = vvTried[tried_bucket][tried_bucket_pos];
+
+ return mapInfo[id_old];
+}
diff --git a/src/addrman.h b/src/addrman.h
index 38da754afb..67423c6c55 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -165,6 +165,9 @@ public:
//! ... in at least this many days
#define ADDRMAN_MIN_FAIL_DAYS 7
+//! how recent a successful connection should be before we allow an address to be evicted from tried
+#define ADDRMAN_REPLACEMENT_HOURS 4
+
//! the maximum percentage of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX_PCT 23
@@ -176,6 +179,9 @@ public:
#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
+//! the maximum number of tried addr collisions to store
+#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10
+
/**
* Stochastical (IP) address manager
*/
@@ -212,6 +218,9 @@ private:
//! last time Good was called (memory only)
int64_t nLastGood;
+ //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discpline used to resolve these collisions.
+ std::set<int> m_tried_collisions;
+
protected:
//! secret key to randomize bucket select with
uint256 nKey;
@@ -239,7 +248,7 @@ protected:
void ClearNew(int nUBucket, int nUBucketPos);
//! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, int64_t nTime);
+ void Good_(const CService &addr, bool test_before_evict, int64_t time);
//! Add an entry to the "new" table.
bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
@@ -250,6 +259,12 @@ protected:
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
CAddrInfo Select_(bool newOnly);
+ //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
+ void ResolveCollisions_();
+
+ //! Return a random to-be-evicted tried table address.
+ CAddrInfo SelectTriedCollision_();
+
//! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
virtual int RandomInt(int nMax);
@@ -537,11 +552,11 @@ public:
}
//! Mark an entry as accessible.
- void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
+ void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
{
LOCK(cs);
Check();
- Good_(addr, nTime);
+ Good_(addr, test_before_evict, nTime);
Check();
}
@@ -554,6 +569,28 @@ public:
Check();
}
+ //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
+ void ResolveCollisions()
+ {
+ LOCK(cs);
+ Check();
+ ResolveCollisions_();
+ Check();
+ }
+
+ //! Randomly select an address in tried that another address is attempting to evict.
+ CAddrInfo SelectTriedCollision()
+ {
+ CAddrInfo ret;
+ {
+ LOCK(cs);
+ Check();
+ ret = SelectTriedCollision_();
+ Check();
+ }
+ return ret;
+ }
+
/**
* Choose an address to connect to.
*/
diff --git a/src/arith_uint256.h b/src/arith_uint256.h
index dc2627592e..3f4cc8c2bf 100644
--- a/src/arith_uint256.h
+++ b/src/arith_uint256.h
@@ -85,7 +85,7 @@ public:
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.pn[i] = ~pn[i];
- ret++;
+ ++ret;
return ret;
}
diff --git a/src/base58.cpp b/src/base58.cpp
index 499afbe382..982e123a1d 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -4,20 +4,12 @@
#include <base58.h>
-#include <bech32.h>
#include <hash.h>
-#include <script/script.h>
#include <uint256.h>
-#include <utilstrencodings.h>
-#include <boost/variant/apply_visitor.hpp>
-#include <boost/variant/static_visitor.hpp>
-
-#include <algorithm>
#include <assert.h>
#include <string.h>
-
/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -151,227 +143,3 @@ bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRe
{
return DecodeBase58Check(str.c_str(), vchRet);
}
-
-CBase58Data::CBase58Data()
-{
- vchVersion.clear();
- vchData.clear();
-}
-
-void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize)
-{
- vchVersion = vchVersionIn;
- vchData.resize(nSize);
- if (!vchData.empty())
- memcpy(vchData.data(), pdata, nSize);
-}
-
-void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend)
-{
- SetData(vchVersionIn, (void*)pbegin, pend - pbegin);
-}
-
-bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes)
-{
- std::vector<unsigned char> vchTemp;
- bool rc58 = DecodeBase58Check(psz, vchTemp);
- if ((!rc58) || (vchTemp.size() < nVersionBytes)) {
- vchData.clear();
- vchVersion.clear();
- return false;
- }
- vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
- vchData.resize(vchTemp.size() - nVersionBytes);
- if (!vchData.empty())
- memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size());
- memory_cleanse(vchTemp.data(), vchTemp.size());
- return true;
-}
-
-bool CBase58Data::SetString(const std::string& str)
-{
- return SetString(str.c_str());
-}
-
-std::string CBase58Data::ToString() const
-{
- std::vector<unsigned char> vch = vchVersion;
- vch.insert(vch.end(), vchData.begin(), vchData.end());
- return EncodeBase58Check(vch);
-}
-
-int CBase58Data::CompareTo(const CBase58Data& b58) const
-{
- if (vchVersion < b58.vchVersion)
- return -1;
- if (vchVersion > b58.vchVersion)
- return 1;
- if (vchData < b58.vchData)
- return -1;
- if (vchData > b58.vchData)
- return 1;
- return 0;
-}
-
-namespace
-{
-class DestinationEncoder : public boost::static_visitor<std::string>
-{
-private:
- const CChainParams& m_params;
-
-public:
- DestinationEncoder(const CChainParams& params) : m_params(params) {}
-
- std::string operator()(const CKeyID& id) const
- {
- std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
- data.insert(data.end(), id.begin(), id.end());
- return EncodeBase58Check(data);
- }
-
- std::string operator()(const CScriptID& id) const
- {
- std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
- data.insert(data.end(), id.begin(), id.end());
- return EncodeBase58Check(data);
- }
-
- std::string operator()(const WitnessV0KeyHash& id) const
- {
- std::vector<unsigned char> data = {0};
- ConvertBits<8, 5, true>(data, id.begin(), id.end());
- return bech32::Encode(m_params.Bech32HRP(), data);
- }
-
- std::string operator()(const WitnessV0ScriptHash& id) const
- {
- std::vector<unsigned char> data = {0};
- ConvertBits<8, 5, true>(data, id.begin(), id.end());
- return bech32::Encode(m_params.Bech32HRP(), data);
- }
-
- std::string operator()(const WitnessUnknown& id) const
- {
- if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
- return {};
- }
- std::vector<unsigned char> data = {(unsigned char)id.version};
- ConvertBits<8, 5, true>(data, id.program, id.program + id.length);
- return bech32::Encode(m_params.Bech32HRP(), data);
- }
-
- std::string operator()(const CNoDestination& no) const { return {}; }
-};
-
-CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
-{
- std::vector<unsigned char> data;
- uint160 hash;
- if (DecodeBase58Check(str, data)) {
- // base58-encoded Bitcoin addresses.
- // Public-key-hash-addresses have version 0 (or 111 testnet).
- // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
- const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
- if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
- std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
- return CKeyID(hash);
- }
- // Script-hash-addresses have version 5 (or 196 testnet).
- // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
- const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
- if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
- std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
- return CScriptID(hash);
- }
- }
- data.clear();
- auto bech = bech32::Decode(str);
- if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) {
- // Bech32 decoding
- int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
- // The rest of the symbols are converted witness program bytes.
- if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) {
- if (version == 0) {
- {
- WitnessV0KeyHash keyid;
- if (data.size() == keyid.size()) {
- std::copy(data.begin(), data.end(), keyid.begin());
- return keyid;
- }
- }
- {
- WitnessV0ScriptHash scriptid;
- if (data.size() == scriptid.size()) {
- std::copy(data.begin(), data.end(), scriptid.begin());
- return scriptid;
- }
- }
- return CNoDestination();
- }
- if (version > 16 || data.size() < 2 || data.size() > 40) {
- return CNoDestination();
- }
- WitnessUnknown unk;
- unk.version = version;
- std::copy(data.begin(), data.end(), unk.program);
- unk.length = data.size();
- return unk;
- }
- }
- return CNoDestination();
-}
-} // namespace
-
-void CBitcoinSecret::SetKey(const CKey& vchSecret)
-{
- assert(vchSecret.IsValid());
- SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size());
- if (vchSecret.IsCompressed())
- vchData.push_back(1);
-}
-
-CKey CBitcoinSecret::GetKey()
-{
- CKey ret;
- assert(vchData.size() >= 32);
- ret.Set(vchData.begin(), vchData.begin() + 32, vchData.size() > 32 && vchData[32] == 1);
- return ret;
-}
-
-bool CBitcoinSecret::IsValid() const
-{
- bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1);
- bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
- return fExpectedFormat && fCorrectVersion;
-}
-
-bool CBitcoinSecret::SetString(const char* pszSecret)
-{
- return CBase58Data::SetString(pszSecret) && IsValid();
-}
-
-bool CBitcoinSecret::SetString(const std::string& strSecret)
-{
- return SetString(strSecret.c_str());
-}
-
-std::string EncodeDestination(const CTxDestination& dest)
-{
- return boost::apply_visitor(DestinationEncoder(Params()), dest);
-}
-
-CTxDestination DecodeDestination(const std::string& str)
-{
- return DecodeDestination(str, Params());
-}
-
-bool IsValidDestinationString(const std::string& str, const CChainParams& params)
-{
- return IsValidDestination(DecodeDestination(str, params));
-}
-
-bool IsValidDestinationString(const std::string& str)
-{
- return IsValidDestinationString(str, Params());
-}
diff --git a/src/base58.h b/src/base58.h
index 39eb4eacc2..8f2833bec9 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,12 +14,6 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
-#include <chainparams.h>
-#include <key.h>
-#include <pubkey.h>
-#include <script/standard.h>
-#include <support/allocators/zeroafterfree.h>
-
#include <string>
#include <vector>
@@ -56,95 +50,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
* Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
+bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
-
-/**
- * Base class for all base58-encoded data
- */
-class CBase58Data
-{
-protected:
- //! the version byte(s)
- std::vector<unsigned char> vchVersion;
-
- //! the actually encoded data
- typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar;
- vector_uchar vchData;
-
- CBase58Data();
- void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize);
- void SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend);
-
-public:
- bool SetString(const char* psz, unsigned int nVersionBytes = 1);
- bool SetString(const std::string& str);
- std::string ToString() const;
- int CompareTo(const CBase58Data& b58) const;
-
- bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; }
- bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; }
- bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; }
- bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; }
- bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
-};
-
-/**
- * A base58-encoded secret key
- */
-class CBitcoinSecret : public CBase58Data
-{
-public:
- void SetKey(const CKey& vchSecret);
- CKey GetKey();
- bool IsValid() const;
- bool SetString(const char* pszSecret);
- bool SetString(const std::string& strSecret);
-
- CBitcoinSecret(const CKey& vchSecret) { SetKey(vchSecret); }
- CBitcoinSecret() {}
-};
-
-template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data
-{
-public:
- void SetKey(const K &key) {
- unsigned char vch[Size];
- key.Encode(vch);
- SetData(Params().Base58Prefix(Type), vch, vch+Size);
- }
-
- K GetKey() {
- K ret;
- if (vchData.size() == Size) {
- // If base58 encoded data does not hold an ext key, return a !IsValid() key
- ret.Decode(vchData.data());
- }
- return ret;
- }
-
- CBitcoinExtKeyBase(const K &key) {
- SetKey(key);
- }
-
- CBitcoinExtKeyBase(const std::string& strBase58c) {
- SetString(strBase58c.c_str(), Params().Base58Prefix(Type).size());
- }
-
- CBitcoinExtKeyBase() {}
-};
-
-typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
-typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey;
-
-std::string EncodeDestination(const CTxDestination& dest);
-CTxDestination DecodeDestination(const std::string& str);
-bool IsValidDestinationString(const std::string& str);
-bool IsValidDestinationString(const std::string& str, const CChainParams& params);
+bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
#endif // BITCOIN_BASE58_H
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 6f438b60e9..98965840c7 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -32,7 +32,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CoinSelection(benchmark::State& state)
{
- const CWallet wallet;
+ const CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
std::vector<COutput> vCoins;
LOCK(wallet.cs_wallet);
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
new file mode 100644
index 0000000000..d0f28d1a3e
--- /dev/null
+++ b/src/bench/prevector.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2015-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.
+
+#include <compat.h>
+#include <prevector.h>
+
+#include <bench/bench.h>
+
+struct nontrivial_t {
+ int x;
+ nontrivial_t() :x(-1) {}
+};
+static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value,
+ "expected nontrivial_t to not be trivially constructible");
+
+typedef unsigned char trivial_t;
+static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value,
+ "expected trivial_t to be trivially constructible");
+
+template <typename T>
+static void PrevectorDestructor(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ for (auto x = 0; x < 1000; ++x) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ t0.resize(28);
+ t1.resize(29);
+ }
+ }
+}
+
+template <typename T>
+static void PrevectorClear(benchmark::State& state)
+{
+
+ while (state.KeepRunning()) {
+ for (auto x = 0; x < 1000; ++x) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ t0.resize(28);
+ t0.clear();
+ t1.resize(29);
+ t0.clear();
+ }
+ }
+}
+
+template <typename T>
+void PrevectorResize(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ for (auto x = 0; x < 1000; ++x) {
+ t0.resize(28);
+ t0.resize(0);
+ t1.resize(29);
+ t1.resize(0);
+ }
+ }
+}
+
+#define PREVECTOR_TEST(name, nontrivops, trivops) \
+ static void Prevector ## name ## Nontrivial(benchmark::State& state) { \
+ PrevectorResize<nontrivial_t>(state); \
+ } \
+ BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \
+ static void Prevector ## name ## Trivial(benchmark::State& state) { \
+ PrevectorResize<trivial_t>(state); \
+ } \
+ BENCHMARK(Prevector ## name ## Trivial, trivops);
+
+PREVECTOR_TEST(Clear, 28300, 88600)
+PREVECTOR_TEST(Destructor, 28800, 88900)
+PREVECTOR_TEST(Resize, 28900, 90300)
diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp
deleted file mode 100644
index 39d0ee5eb1..0000000000
--- a/src/bench/prevector_destructor.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2015-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.
-
-#include <bench/bench.h>
-#include <prevector.h>
-
-static void PrevectorDestructor(benchmark::State& state)
-{
- while (state.KeepRunning()) {
- for (auto x = 0; x < 1000; ++x) {
- prevector<28, unsigned char> t0;
- prevector<28, unsigned char> t1;
- t0.resize(28);
- t1.resize(29);
- }
- }
-}
-
-static void PrevectorClear(benchmark::State& state)
-{
-
- while (state.KeepRunning()) {
- for (auto x = 0; x < 1000; ++x) {
- prevector<28, unsigned char> t0;
- prevector<28, unsigned char> t1;
- t0.resize(28);
- t0.clear();
- t1.resize(29);
- t0.clear();
- }
- }
-}
-
-BENCHMARK(PrevectorDestructor, 5700);
-BENCHMARK(PrevectorClear, 5600);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index a60d3b3b6d..41f1e5786c 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -46,7 +46,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
- strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)"));
+ strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)"));
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password."));
strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.")));
@@ -339,8 +339,8 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co
// check if we should use a special wallet endpoint
std::string endpoint = "/";
- std::string walletName = gArgs.GetArg("-rpcwallet", "");
- if (!walletName.empty()) {
+ if (!gArgs.GetArgs("-rpcwallet").empty()) {
+ std::string walletName = gArgs.GetArg("-rpcwallet", "");
char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false);
if (encodedURI) {
endpoint = "/wallet/"+ std::string(encodedURI);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index a9f7264f68..fcd836fb45 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -6,11 +6,11 @@
#include <config/bitcoin-config.h>
#endif
-#include <base58.h>
#include <clientversion.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <core_io.h>
+#include <key_io.h>
#include <keystore.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -563,12 +563,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr())
throw std::runtime_error("privatekey not a std::string");
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
- if (!fGood)
+ CKey key = DecodeSecret(keysObj[kidx].getValStr());
+ if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid");
-
- CKey key = vchSecret.GetKey();
+ }
tempKeystore.AddKey(key);
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 6eb223171f..c2b3480f9d 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -175,6 +175,9 @@ public:
// (the tx=... number in the SetBestChain debug.log lines)
3.5 // * estimated number of transactions per second after that timestamp
};
+
+ /* disable fallback fee on mainnet */
+ m_fallback_fee_enabled = false;
}
};
@@ -266,6 +269,8 @@ public:
0.09
};
+ /* enable fallback fee on testnet */
+ m_fallback_fee_enabled = true;
}
};
@@ -343,6 +348,9 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "bcrt";
+
+ /* enable fallback fee on regtest */
+ m_fallback_fee_enabled = true;
}
};
diff --git a/src/chainparams.h b/src/chainparams.h
index d478da9891..6b1f813afb 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -65,6 +65,8 @@ public:
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
+ /** Return true if the fallback fee is by default enabled for this network */
+ bool IsFallbackFeeEnabled() const { return m_fallback_fee_enabled; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
@@ -91,6 +93,7 @@ protected:
bool fMineBlocksOnDemand;
CCheckpointData checkpointData;
ChainTxData chainTxData;
+ bool m_fallback_fee_enabled;
};
/**
diff --git a/src/compat.h b/src/compat.h
index aae84b1181..8a0f901304 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -10,6 +10,16 @@
#include <config/bitcoin-config.h>
#endif
+#include <type_traits>
+
+// GCC 4.8 is missing some C++11 type_traits,
+// https://www.gnu.org/software/gcc/gcc-5/changes.html
+#if defined(__GNUC__) && __GNUC__ < 5
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial
+#else
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible
+#endif
+
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
diff --git a/src/core_write.cpp b/src/core_write.cpp
index ab6918e41d..91742b7d1b 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -4,9 +4,9 @@
#include <core_io.h>
-#include <base58.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
+#include <key_io.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
diff --git a/src/crypto/common.h b/src/crypto/common.h
index 825b430978..6e9d6dc82a 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -82,12 +82,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
/** 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 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 HAVE_DECL___BUILTIN_CLZLL
if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
}
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 4e1e403f69..6cac625abc 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -89,6 +89,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
}
CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
+ : m_name(fs::basename(path))
{
penv = nullptr;
readoptions.verify_checksums = true;
@@ -155,11 +156,30 @@ CDBWrapper::~CDBWrapper()
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
{
+ const bool log_memory = LogAcceptCategory(BCLog::LEVELDB);
+ double mem_before = 0;
+ if (log_memory) {
+ mem_before = DynamicMemoryUsage() / 1024 / 1024;
+ }
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
dbwrapper_private::HandleError(status);
+ if (log_memory) {
+ double mem_after = DynamicMemoryUsage() / 1024 / 1024;
+ LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n",
+ m_name, mem_before, mem_after);
+ }
return true;
}
+size_t CDBWrapper::DynamicMemoryUsage() const {
+ std::string memory;
+ if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) {
+ LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
+ return 0;
+ }
+ return stoul(memory);
+}
+
// Prefixed with null character to avoid collisions with other keys
//
// We must use a string constructor which specifies length so that we copy
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index a29938ce33..6f80eedc7a 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -198,6 +198,9 @@ private:
//! the database itself
leveldb::DB* pdb;
+ //! the name of this database
+ std::string m_name;
+
//! a key used for optional XOR-obfuscation of the database
std::vector<unsigned char> obfuscate_key;
@@ -284,6 +287,9 @@ public:
bool WriteBatch(CDBBatch& batch, bool fSync = false);
+ // Get an estimate of LevelDB memory usage (in bytes).
+ size_t DynamicMemoryUsage() const;
+
// not available for LevelDB; provide for compatibility with BDB
bool Flush()
{
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 5e9e419744..82ae733006 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -4,9 +4,9 @@
#include <httprpc.h>
-#include <base58.h>
#include <chainparams.h>
#include <httpserver.h>
+#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <random.h>
diff --git a/src/init.cpp b/src/init.cpp
index 1cc5c5f9c7..bc23d0ff83 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -448,6 +448,7 @@ std::string HelpMessage(HelpMessageMode mode)
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));
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)");
+ strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost");
}
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
@@ -925,7 +926,7 @@ bool AppInitParameterInteraction()
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
- nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0);
+ nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
@@ -1082,7 +1083,7 @@ bool AppInitParameterInteraction()
if (gArgs.IsArgSet("-dustrelayfee"))
{
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n)
+ if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n))
return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
@@ -1223,7 +1224,7 @@ bool AppInitMain()
}
if (!fLogTimestamps)
- LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
+ LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
diff --git a/src/key.cpp b/src/key.cpp
index ffed989be1..042e687772 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -170,7 +170,7 @@ CPrivKey CKey::GetPrivKey() const {
size_t privkeylen;
privkey.resize(PRIVATE_KEY_SIZE);
privkeylen = PRIVATE_KEY_SIZE;
- ret = ec_privkey_export_der(secp256k1_context_sign, (unsigned char*) privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
+ ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
assert(ret);
privkey.resize(privkeylen);
return privkey;
@@ -199,7 +199,7 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_
secp256k1_ecdsa_signature sig;
int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr);
assert(ret);
- secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)vchSig.data(), &nSigLen, &sig);
+ secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig);
vchSig.resize(nSigLen);
return true;
}
@@ -226,7 +226,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
secp256k1_ecdsa_recoverable_signature sig;
int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr);
assert(ret);
- secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, (unsigned char*)&vchSig[1], &rec, &sig);
+ secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &sig);
assert(ret);
assert(rec != -1);
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
diff --git a/src/key_io.cpp b/src/key_io.cpp
new file mode 100644
index 0000000000..c2dc511989
--- /dev/null
+++ b/src/key_io.cpp
@@ -0,0 +1,227 @@
+// Copyright (c) 2014-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key_io.h>
+
+#include <base58.h>
+#include <bech32.h>
+#include <script/script.h>
+#include <utilstrencodings.h>
+
+#include <boost/variant/apply_visitor.hpp>
+#include <boost/variant/static_visitor.hpp>
+
+#include <assert.h>
+#include <string.h>
+#include <algorithm>
+
+namespace
+{
+class DestinationEncoder : public boost::static_visitor<std::string>
+{
+private:
+ const CChainParams& m_params;
+
+public:
+ DestinationEncoder(const CChainParams& params) : m_params(params) {}
+
+ std::string operator()(const CKeyID& id) const
+ {
+ std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
+ data.insert(data.end(), id.begin(), id.end());
+ return EncodeBase58Check(data);
+ }
+
+ std::string operator()(const CScriptID& id) const
+ {
+ std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ data.insert(data.end(), id.begin(), id.end());
+ return EncodeBase58Check(data);
+ }
+
+ std::string operator()(const WitnessV0KeyHash& id) const
+ {
+ std::vector<unsigned char> data = {0};
+ data.reserve(33);
+ ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
+
+ std::string operator()(const WitnessV0ScriptHash& id) const
+ {
+ std::vector<unsigned char> data = {0};
+ data.reserve(53);
+ ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
+
+ std::string operator()(const WitnessUnknown& id) const
+ {
+ if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
+ return {};
+ }
+ std::vector<unsigned char> data = {(unsigned char)id.version};
+ data.reserve(1 + (id.length * 8 + 4) / 5);
+ ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
+
+ std::string operator()(const CNoDestination& no) const { return {}; }
+};
+
+CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
+{
+ std::vector<unsigned char> data;
+ uint160 hash;
+ if (DecodeBase58Check(str, data)) {
+ // base58-encoded Bitcoin addresses.
+ // Public-key-hash-addresses have version 0 (or 111 testnet).
+ // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
+ const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
+ if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
+ std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
+ return CKeyID(hash);
+ }
+ // Script-hash-addresses have version 5 (or 196 testnet).
+ // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
+ const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
+ std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
+ return CScriptID(hash);
+ }
+ }
+ data.clear();
+ auto bech = bech32::Decode(str);
+ if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) {
+ // Bech32 decoding
+ int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
+ // The rest of the symbols are converted witness program bytes.
+ data.reserve(((bech.second.size() - 1) * 5) / 8);
+ if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) {
+ if (version == 0) {
+ {
+ WitnessV0KeyHash keyid;
+ if (data.size() == keyid.size()) {
+ std::copy(data.begin(), data.end(), keyid.begin());
+ return keyid;
+ }
+ }
+ {
+ WitnessV0ScriptHash scriptid;
+ if (data.size() == scriptid.size()) {
+ std::copy(data.begin(), data.end(), scriptid.begin());
+ return scriptid;
+ }
+ }
+ return CNoDestination();
+ }
+ if (version > 16 || data.size() < 2 || data.size() > 40) {
+ return CNoDestination();
+ }
+ WitnessUnknown unk;
+ unk.version = version;
+ std::copy(data.begin(), data.end(), unk.program);
+ unk.length = data.size();
+ return unk;
+ }
+ }
+ return CNoDestination();
+}
+} // namespace
+
+CKey DecodeSecret(const std::string& str)
+{
+ CKey key;
+ std::vector<unsigned char> data;
+ if (DecodeBase58Check(str, data)) {
+ const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
+ if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
+ std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
+ bool compressed = data.size() == 33 + privkey_prefix.size();
+ key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed);
+ }
+ }
+ memory_cleanse(data.data(), data.size());
+ return key;
+}
+
+std::string EncodeSecret(const CKey& key)
+{
+ assert(key.IsValid());
+ std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY);
+ data.insert(data.end(), key.begin(), key.end());
+ if (key.IsCompressed()) {
+ data.push_back(1);
+ }
+ std::string ret = EncodeBase58Check(data);
+ memory_cleanse(data.data(), data.size());
+ return ret;
+}
+
+CExtPubKey DecodeExtPubKey(const std::string& str)
+{
+ CExtPubKey key;
+ std::vector<unsigned char> data;
+ if (DecodeBase58Check(str, data)) {
+ const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
+ if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
+ key.Decode(data.data() + prefix.size());
+ }
+ }
+ return key;
+}
+
+std::string EncodeExtPubKey(const CExtPubKey& key)
+{
+ std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
+ size_t size = data.size();
+ data.resize(size + BIP32_EXTKEY_SIZE);
+ key.Encode(data.data() + size);
+ std::string ret = EncodeBase58Check(data);
+ return ret;
+}
+
+CExtKey DecodeExtKey(const std::string& str)
+{
+ CExtKey key;
+ std::vector<unsigned char> data;
+ if (DecodeBase58Check(str, data)) {
+ const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
+ if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
+ key.Decode(data.data() + prefix.size());
+ }
+ }
+ return key;
+}
+
+std::string EncodeExtKey(const CExtKey& key)
+{
+ std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
+ size_t size = data.size();
+ data.resize(size + BIP32_EXTKEY_SIZE);
+ key.Encode(data.data() + size);
+ std::string ret = EncodeBase58Check(data);
+ memory_cleanse(data.data(), data.size());
+ return ret;
+}
+
+std::string EncodeDestination(const CTxDestination& dest)
+{
+ return boost::apply_visitor(DestinationEncoder(Params()), dest);
+}
+
+CTxDestination DecodeDestination(const std::string& str)
+{
+ return DecodeDestination(str, Params());
+}
+
+bool IsValidDestinationString(const std::string& str, const CChainParams& params)
+{
+ return IsValidDestination(DecodeDestination(str, params));
+}
+
+bool IsValidDestinationString(const std::string& str)
+{
+ return IsValidDestinationString(str, Params());
+}
diff --git a/src/key_io.h b/src/key_io.h
new file mode 100644
index 0000000000..6fc9a8059a
--- /dev/null
+++ b/src/key_io.h
@@ -0,0 +1,29 @@
+// 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_KEYIO_H
+#define BITCOIN_KEYIO_H
+
+#include <chainparams.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/standard.h>
+
+#include <string>
+
+CKey DecodeSecret(const std::string& str);
+std::string EncodeSecret(const CKey& key);
+
+CExtKey DecodeExtKey(const std::string& str);
+std::string EncodeExtKey(const CExtKey& extkey);
+CExtPubKey DecodeExtPubKey(const std::string& str);
+std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
+
+std::string EncodeDestination(const CTxDestination& dest);
+CTxDestination DecodeDestination(const std::string& str);
+bool IsValidDestinationString(const std::string& str);
+bool IsValidDestinationString(const std::string& str, const CChainParams& params);
+
+#endif // BITCOIN_KEYIO_H
diff --git a/src/miner.cpp b/src/miner.cpp
index dda52790c6..fcb376c6cb 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -30,12 +30,6 @@
#include <queue>
#include <utility>
-//////////////////////////////////////////////////////////////////////////////
-//
-// BitcoinMiner
-//
-
-//
// Unconfirmed transactions in the memory pool often depend on other
// transactions in the memory pool. When we select transactions from the
// pool, we select by highest fee rate of a transaction combined with all
diff --git a/src/net.cpp b/src/net.cpp
index 201914685c..53a0a9b180 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -181,6 +181,10 @@ void AdvertiseLocal(CNode *pnode)
if (fListen && pnode->fSuccessfullyConnected)
{
CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
+ if (gArgs.GetBoolArg("-addrmantest", false)) {
+ // use IPv4 loopback during addrmantest
+ addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
+ }
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
@@ -189,7 +193,7 @@ void AdvertiseLocal(CNode *pnode)
{
addrLocal.SetIP(pnode->GetAddrLocal());
}
- if (addrLocal.IsRoutable())
+ if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
FastRandomContext insecure_rand;
@@ -1627,7 +1631,8 @@ void CConnman::ThreadDNSAddressSeed()
if (!resolveSource.SetInternal(host)) {
continue;
}
- if (LookupHost(host.c_str(), vIPs, 0, true))
+ unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed
+ if (LookupHost(host.c_str(), vIPs, nMaxIPs, true))
{
for (const CNetAddr& ip : vIPs)
{
@@ -1824,11 +1829,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
}
+ addrman.ResolveCollisions();
+
int64_t nANow = GetAdjustedTime();
int nTries = 0;
while (!interruptNet)
{
- CAddrInfo addr = addrman.Select(fFeeler);
+ CAddrInfo addr = addrman.SelectTriedCollision();
+
+ // SelectTriedCollision returns an invalid address if it is empty.
+ if (!fFeeler || !addr.IsValid()) {
+ addr = addrman.Select(fFeeler);
+ }
// if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
@@ -2718,6 +2730,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
fOneShot = false;
m_manual_connection = false;
fClient = false; // set by version message
+ m_limited_node = false; // set by version message
fFeeler = false;
fSuccessfullyConnected = false;
fDisconnect = false;
@@ -2782,7 +2795,7 @@ void CNode::AskFor(const CInv& inv)
nRequestTime = it->second;
else
nRequestTime = 0;
- LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id);
+ LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, FormatISO8601Time(nRequestTime/1000000), id);
// Make sure not to reuse time indexes to keep things in the same order
int64_t nNow = GetTimeMicros() - 1000000;
diff --git a/src/net.h b/src/net.h
index 8378a303b8..96f04d83e0 100644
--- a/src/net.h
+++ b/src/net.h
@@ -641,6 +641,7 @@ public:
bool fOneShot;
bool m_manual_connection;
bool fClient;
+ bool m_limited_node; //after BIP159
const bool fInbound;
std::atomic_bool fSuccessfullyConnected;
std::atomic_bool fDisconnect;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index bf9307727a..482a206c8b 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -401,6 +401,12 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
+/**
+ * When a peer sends us a valid block, instruct it to announce blocks to us
+ * using CMPCTBLOCK if possible by adding its nodeid to the end of
+ * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
+ * removing the first element if necessary.
+ */
void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
@@ -749,7 +755,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
return nEvicted;
}
-// Requires cs_main.
+/**
+ * Mark a misbehaving peer to be banned depending upon the value of `-banscore`.
+ *
+ * Requires cs_main.
+ */
void Misbehaving(NodeId pnode, int howmuch, const std::string& message)
{
if (howmuch == 0)
@@ -808,6 +818,10 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &schedu
scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000);
}
+/**
+ * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
+ * block. Also save the time of the last tip update.
+ */
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
LOCK(g_cs_orphans);
@@ -828,7 +842,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
}
- // Erase orphan transactions include or precluded by this block
+ // Erase orphan transactions included or precluded by this block
if (vOrphanErase.size()) {
int nErased = 0;
for (uint256 &orphanHash : vOrphanErase) {
@@ -847,6 +861,10 @@ static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_bloc
static uint256 most_recent_block_hash;
static bool fWitnessesPresentInMostRecentCompactBlock;
+/**
+ * Maintain state about the best-seen block and fast-announce a compact block
+ * to compatible peers.
+ */
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);
const CNetMsgMaker msgMaker(PROTOCOL_VERSION);
@@ -888,10 +906,15 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
});
}
+/**
+ * Update our best height and announce any block hashes which weren't previously
+ * in chainActive to our peers.
+ */
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
connman->SetBestHeight(nNewHeight);
+ SetServiceFlagsIBDCache(!fInitialDownload);
if (!fInitialDownload) {
// Find the hashes of all blocks that weren't previously in the best chain.
std::vector<uint256> vHashes;
@@ -919,6 +942,10 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
nTimeBestReceived = GetTime();
}
+/**
+ * Handle invalid block rejection and consequent peer banning, maintain which
+ * peers announce compact blocks.
+ */
void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
LOCK(cs_main);
@@ -1642,7 +1669,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->cleanSubVer = cleanSubVer;
}
pfrom->nStartingHeight = nStartingHeight;
- pfrom->fClient = !(nServices & NODE_NETWORK);
+
+ // set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients"
+ pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
+
+ // set nodes not capable of serving the complete blockchain history as "limited nodes"
+ pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
+
{
LOCK(pfrom->cs_filter);
pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
@@ -1801,7 +1834,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We only bother storing full nodes, though this may include
// things which we would not make an outbound connection to, in
// part because we may make feeler connections to them.
- if (!MayHaveUsefulAddressDB(addr.nServices))
+ if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices))
continue;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
@@ -3611,7 +3644,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
diff --git a/src/net_processing.h b/src/net_processing.h
index b534ef01c3..ff1ebc59da 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -42,13 +42,26 @@ private:
public:
explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
+ /**
+ * Overridden from CValidationInterface.
+ */
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ /**
+ * Overridden from CValidationInterface.
+ */
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
+ /**
+ * Overridden from CValidationInterface.
+ */
void BlockChecked(const CBlock& block, const CValidationState& state) override;
+ /**
+ * Overridden from CValidationInterface.
+ */
void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
-
+ /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */
void InitializeNode(CNode* pnode) override;
+ /** Handle removal of a peer by updating various state and removing it from mapNodeState */
void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
@@ -61,8 +74,11 @@ public:
*/
bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override;
+ /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
void ConsiderEviction(CNode *pto, int64_t time_in_seconds);
+ /** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
+ /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(int64_t time_in_seconds);
private:
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 5be3fe34f8..3ea3141d5e 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -139,7 +139,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault,
if (pszName[0] == 0)
return false;
int port = portDefault;
- std::string hostname = "";
+ std::string hostname;
SplitHostPort(std::string(pszName), port, hostname);
std::vector<CNetAddr> vIP;
diff --git a/src/prevector.h b/src/prevector.h
index f8d6a09145..103ead82cc 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -10,9 +10,12 @@
#include <stdint.h>
#include <string.h>
+#include <cstddef>
#include <iterator>
#include <type_traits>
+#include <compat.h>
+
#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
@@ -194,16 +197,42 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
+ void fill(T* dst, ptrdiff_t count) {
+ if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) {
+ // The most common use of prevector is where T=unsigned char. For
+ // trivially constructible types, we can use memset() to avoid
+ // looping.
+ ::memset(dst, 0, count * sizeof(T));
+ } else {
+ for (auto i = 0; i < count; ++i) {
+ new(static_cast<void*>(dst + i)) T();
+ }
+ }
+ }
+
+ void fill(T* dst, ptrdiff_t count, const T& value) {
+ for (auto i = 0; i < count; ++i) {
+ new(static_cast<void*>(dst + i)) T(value);
+ }
+ }
+
+ template<typename InputIterator>
+ void fill(T* dst, InputIterator first, InputIterator last) {
+ while (first != last) {
+ new(static_cast<void*>(dst)) T(*first);
+ ++dst;
+ ++first;
+ }
+ }
+
public:
void assign(size_type n, const T& val) {
clear();
if (capacity() < n) {
change_capacity(n);
}
- while (size() < n) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(val);
- }
+ _size += n;
+ fill(item_ptr(0), n, val);
}
template<typename InputIterator>
@@ -213,11 +242,8 @@ public:
if (capacity() < n) {
change_capacity(n);
}
- while (first != last) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
- ++first;
- }
+ _size += n;
+ fill(item_ptr(0), first, last);
}
prevector() : _size(0), _union{{}} {}
@@ -228,31 +254,23 @@ public:
explicit prevector(size_type n, const T& val = T()) : _size(0) {
change_capacity(n);
- while (size() < n) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(val);
- }
+ _size += n;
+ fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) : _size(0) {
size_type n = last - first;
change_capacity(n);
- while (first != last) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
- ++first;
- }
+ _size += n;
+ fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
- change_capacity(other.size());
- const_iterator it = other.begin();
- while (it != other.end()) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
- ++it;
- }
+ size_type n = other.size();
+ change_capacity(n);
+ _size += n;
+ fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) : _size(0) {
@@ -263,14 +281,7 @@ public:
if (&other == this) {
return *this;
}
- resize(0);
- change_capacity(other.size());
- const_iterator it = other.begin();
- while (it != other.end()) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
- ++it;
- }
+ assign(other.begin(), other.end());
return *this;
}
@@ -314,16 +325,20 @@ public:
}
void resize(size_type new_size) {
- if (size() > new_size) {
+ size_type cur_size = size();
+ if (cur_size == new_size) {
+ return;
+ }
+ if (cur_size > new_size) {
erase(item_ptr(new_size), end());
+ return;
}
if (new_size > capacity()) {
change_capacity(new_size);
}
- while (size() < new_size) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T();
- }
+ ptrdiff_t increase = new_size - cur_size;
+ fill(item_ptr(cur_size), increase);
+ _size += increase;
}
void reserve(size_type new_capacity) {
@@ -346,10 +361,11 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + 1, ptr, (size() - p) * sizeof(T));
_size++;
- new(static_cast<void*>(item_ptr(p))) T(value);
- return iterator(item_ptr(p));
+ new(static_cast<void*>(ptr)) T(value);
+ return iterator(ptr);
}
void insert(iterator pos, size_type count, const T& value) {
@@ -358,11 +374,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
- for (size_type i = 0; i < count; i++) {
- new(static_cast<void*>(item_ptr(p + i))) T(value);
- }
+ fill(item_ptr(p), count, value);
}
template<typename InputIterator>
@@ -373,13 +388,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
- while (first != last) {
- new(static_cast<void*>(item_ptr(p))) T(*first);
- ++p;
- ++first;
- }
+ fill(ptr, first, last);
}
iterator erase(iterator pos) {
diff --git a/src/protocol.cpp b/src/protocol.cpp
index c412ad9ffe..2ec26fbd3e 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -12,6 +12,8 @@
# include <arpa/inet.h>
#endif
+static std::atomic<bool> g_initial_block_download_completed(false);
+
namespace NetMsgType {
const char *VERSION="version";
const char *VERACK="verack";
@@ -127,6 +129,17 @@ bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const
}
+ServiceFlags GetDesirableServiceFlags(ServiceFlags services) {
+ if ((services & NODE_NETWORK_LIMITED) && g_initial_block_download_completed) {
+ return ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS);
+ }
+ return ServiceFlags(NODE_NETWORK | NODE_WITNESS);
+}
+
+void SetServiceFlagsIBDCache(bool state) {
+ g_initial_block_download_completed = state;
+}
+
CAddress::CAddress() : CService()
{
diff --git a/src/protocol.h b/src/protocol.h
index 42eb57e4f0..e518d11944 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -15,6 +15,7 @@
#include <uint256.h>
#include <version.h>
+#include <atomic>
#include <stdint.h>
#include <string>
@@ -301,9 +302,10 @@ enum ServiceFlags : uint64_t {
* If the NODE_NONE return value is changed, contrib/seeds/makeseeds.py
* should be updated appropriately to filter for the same nodes.
*/
-static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) {
- return ServiceFlags(NODE_NETWORK | NODE_WITNESS);
-}
+ServiceFlags GetDesirableServiceFlags(ServiceFlags services);
+
+/** Set the current IBD status in order to figure out the desirable service flags */
+void SetServiceFlagsIBDCache(bool status);
/**
* A shortcut for (services & GetDesirableServiceFlags(services))
@@ -316,10 +318,10 @@ static inline bool HasAllDesirableServiceFlags(ServiceFlags services) {
/**
* Checks if a peer with the given service flags may be capable of having a
- * robust address-storage DB. Currently an alias for checking NODE_NETWORK.
+ * robust address-storage DB.
*/
static inline bool MayHaveUsefulAddressDB(ServiceFlags services) {
- return services & NODE_NETWORK;
+ return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED);
}
/** A CService with information about it as peer */
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index ffb5bff4de..4f9a79d654 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -7,10 +7,9 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
-#include <base58.h>
+#include <key_io.h>
#include <wallet/wallet.h>
-
#include <QFont>
#include <QDebug>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 06e1f1a37c..ab381bfb5d 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -29,6 +29,7 @@
#include <init.h>
#include <rpc/server.h>
#include <ui_interface.h>
+#include <uint256.h>
#include <util.h>
#include <warnings.h>
@@ -80,6 +81,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
+Q_DECLARE_METATYPE(uint256)
static void InitMessage(const std::string &message)
{
diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp
index 395ab447d2..6a76358a78 100644
--- a/src/qt/bitcoinaddressvalidator.cpp
+++ b/src/qt/bitcoinaddressvalidator.cpp
@@ -4,7 +4,7 @@
#include <qt/bitcoinaddressvalidator.h>
-#include <base58.h>
+#include <key_io.h>
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 4e868b7c17..427eb95a84 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -923,6 +923,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
showNormalIfMinimized();
QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this);
+ mBox.setTextFormat(Qt::PlainText);
int r = mBox.exec();
if (ret != nullptr)
*ret = r == QMessageBox::Ok;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 8d2e5619e0..b83755ab30 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -15,6 +15,7 @@
#include <wallet/coincontrol.h>
#include <init.h>
+#include <key_io.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <validation.h> // For mempool
diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
index fdc52dc455..b5a69c578d 100644
--- a/src/qt/forms/modaloverlay.ui
+++ b/src/qt/forms/modaloverlay.ui
@@ -351,6 +351,12 @@ QLabel { color: rgb(40,40,40); }</string>
<property name="text">
<string>Hide</string>
</property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
</layout>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 195a5560f7..6b31ddea90 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -848,7 +848,9 @@
<item>
<widget class="QLabel" name="labelCustomPerKilobyte">
<property name="toolTip">
- <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;total at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string>
+ <string>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</string>
</property>
<property name="text">
<string>per kilobyte</string>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index a46e0561b9..7c3c68bfef 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -9,7 +9,10 @@
#include <qt/qvalidatedlineedit.h>
#include <qt/walletmodel.h>
+#include <base58.h>
+#include <chainparams.h>
#include <primitives/transaction.h>
+#include <key_io.h>
#include <init.h>
#include <policy/policy.h>
#include <protocol.h>
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index b0ef475b35..357e98a53c 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -9,6 +9,7 @@
#include <qt/paymentrequestplus.h>
+#include <script/script.h>
#include <util.h>
#include <stdexcept>
diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h
index be3923304f..b1b60cf582 100644
--- a/src/qt/paymentrequestplus.h
+++ b/src/qt/paymentrequestplus.h
@@ -10,7 +10,8 @@
#include <qt/paymentrequest.pb.h>
#pragma GCC diagnostic pop
-#include <base58.h>
+#include <amount.h>
+#include <script/script.h>
#include <openssl/x509.h>
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index bc69d4f945..4b6fdc8d57 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -8,9 +8,9 @@
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
-#include <base58.h>
#include <chainparams.h>
#include <policy/policy.h>
+#include <key_io.h>
#include <ui_interface.h>
#include <util.h>
#include <wallet/wallet.h>
@@ -770,7 +770,7 @@ bool PaymentServer::verifyExpired(const payments::PaymentDetails& requestDetails
{
bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime());
if (fVerified) {
- const QString requestExpires = QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires()));
+ const QString requestExpires = QString::fromStdString(FormatISO8601DateTime((int64_t)requestDetails.expires()));
qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".")
.arg(__func__)
.arg(requestExpires);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 871822ccb4..ec7edd48cd 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -14,8 +14,8 @@
#include <qt/platformstyle.h>
#include <qt/sendcoinsentry.h>
-#include <base58.h>
#include <chainparams.h>
+#include <key_io.h>
#include <wallet/coincontrol.h>
#include <validation.h> // mempool and minRelayTxFee
#include <ui_interface.h>
@@ -369,12 +369,19 @@ void SendCoinsDialog::on_sendButton_clicked()
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
+ Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash());
}
fNewRecipientAllowed = true;
}
void SendCoinsDialog::clear()
{
+ // Clear coin control settings
+ CoinControlDialog::coinControl()->UnSelectAll();
+ ui->checkBoxCoinControlChange->setChecked(false);
+ ui->lineEditCoinControlChange->clear();
+ coinControlUpdateLabels();
+
// Remove entries until only one left
while(ui->entries->count())
{
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 7c27785d12..48885bbcad 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -54,6 +54,9 @@ public Q_SLOTS:
void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+Q_SIGNALS:
+ void coinsSent(const uint256& txid);
+
private:
Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 364dcd6f45..8dade8df79 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -10,8 +10,8 @@
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
-#include <base58.h>
#include <init.h>
+#include <key_io.h>
#include <validation.h> // For strMessageMagic
#include <wallet/wallet.h>
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 6e80625123..29ef4b4c9e 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -8,6 +8,7 @@
#include <qt/test/paymentrequestdata.h>
#include <amount.h>
+#include <chainparams.h>
#include <random.h>
#include <script/script.h>
#include <script/standard.h>
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index cd49292138..976aadc0af 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -10,6 +10,7 @@
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
+#include <key_io.h>
#include <test/test_bitcoin.h>
#include <validation.h>
#include <wallet/wallet.h>
@@ -157,9 +158,7 @@ void TestGUI()
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
- bitdb.MakeMock();
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
- CWallet wallet(std::move(dbw));
+ CWallet wallet("mock", CWalletDBWrapper::CreateMock());
bool firstRun;
wallet.LoadWallet(firstRun);
{
@@ -260,9 +259,6 @@ void TestGUI()
QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
removeRequestButton->click();
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
-
- bitdb.Flush(true);
- bitdb.Reset();
}
}
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index c1d28be0ab..ec5a66bc9f 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -9,14 +9,15 @@
#include <qt/paymentserver.h>
#include <qt/transactionrecord.h>
-#include <base58.h>
#include <consensus/consensus.h>
+#include <key_io.h>
#include <validation.h>
#include <script/script.h>
#include <timedata.h>
#include <util.h>
#include <wallet/db.h>
#include <wallet/wallet.h>
+#include <policy/policy.h>
#include <stdint.h>
#include <string>
@@ -239,8 +240,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
- strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
+ strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>";
strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
+ strHTML += "<b>" + tr("Transaction virtual size") + ":</b> " + QString::number(GetVirtualTransactionSize(*wtx.tx)) + " bytes<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example)
diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp
index 161fccd462..7bf4d3351c 100644
--- a/src/qt/transactiondescdialog.cpp
+++ b/src/qt/transactiondescdialog.cpp
@@ -14,7 +14,7 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa
ui(new Ui::TransactionDescDialog)
{
ui->setupUi(this);
- setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxIDRole).toString()));
+ setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxHashRole).toString()));
QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString();
ui->detailText->setHtml(desc);
}
diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp
index 39d03fa547..a702461f7a 100644
--- a/src/qt/transactionfilterproxy.cpp
+++ b/src/qt/transactionfilterproxy.cpp
@@ -36,7 +36,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool();
QString address = index.data(TransactionTableModel::AddressRole).toString();
QString label = index.data(TransactionTableModel::LabelRole).toString();
- QString txid = index.data(TransactionTableModel::TxIDRole).toString();
+ QString txid = index.data(TransactionTableModel::TxHashRole).toString();
qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong());
int status = index.data(TransactionTableModel::StatusRole).toInt();
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index de3e885e8f..19cdb0fdea 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -4,8 +4,8 @@
#include <qt/transactionrecord.h>
-#include <base58.h>
#include <consensus/consensus.h>
+#include <key_io.h>
#include <validation.h>
#include <timedata.h>
#include <wallet/wallet.h>
@@ -254,7 +254,7 @@ bool TransactionRecord::statusUpdateNeeded() const
return status.cur_num_blocks != chainActive.Height() || status.needsUpdate;
}
-QString TransactionRecord::getTxID() const
+QString TransactionRecord::getTxHash() const
{
return QString::fromStdString(hash.ToString());
}
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 29a3cd8de7..5321d05d15 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -129,7 +129,7 @@ public:
bool involvesWatchAddress;
/** Return the unique identifier for this transaction (part) */
- QString getTxID() const;
+ QString getTxHash() const;
/** Return the output index of the subtransaction */
int getOutputIndex() const;
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 626d4c0bdc..84800125fe 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -615,10 +615,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
case AmountRole:
return qint64(rec->credit + rec->debit);
- case TxIDRole:
- return rec->getTxID();
case TxHashRole:
- return QString::fromStdString(rec->hash.ToString());
+ return rec->getTxHash();
case TxHexRole:
return priv->getTxHex(rec);
case TxPlainTextRole:
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index 8f58962d17..781874d160 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -56,8 +56,6 @@ public:
LabelRole,
/** Net amount of transaction */
AmountRole,
- /** Unique identifier */
- TxIDRole,
/** Transaction hash */
TxHashRole,
/** Transaction data, hex-encoded */
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 88f8f463bc..26391452da 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -263,8 +263,7 @@ void TransactionView::setModel(WalletModel *_model)
void TransactionView::chooseDate(int idx)
{
- if(!transactionProxyModel)
- return;
+ if (!transactionProxyModel) return;
QDate current = QDate::currentDate();
dateRangeWidget->setVisible(false);
switch(dateWidget->itemData(idx).toInt())
@@ -372,7 +371,7 @@ void TransactionView::exportClicked()
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole);
- writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
+ writer.addColumn(tr("ID"), 0, TransactionTableModel::TxHashRole);
if(!writer.write()) {
Q_EMIT message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename),
@@ -456,7 +455,7 @@ void TransactionView::copyAmount()
void TransactionView::copyTxID()
{
- GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole);
+ GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxHashRole);
}
void TransactionView::copyTxHex()
@@ -592,6 +591,32 @@ void TransactionView::focusTransaction(const QModelIndex &idx)
transactionView->setFocus();
}
+void TransactionView::focusTransaction(const uint256& txid)
+{
+ if (!transactionProxyModel)
+ return;
+
+ const QModelIndexList results = this->model->getTransactionTableModel()->match(
+ this->model->getTransactionTableModel()->index(0,0),
+ TransactionTableModel::TxHashRole,
+ QString::fromStdString(txid.ToString()), -1);
+
+ transactionView->setFocus();
+ transactionView->selectionModel()->clearSelection();
+ for (const QModelIndex& index : results) {
+ const QModelIndex targetIndex = transactionProxyModel->mapFromSource(index);
+ transactionView->selectionModel()->select(
+ targetIndex,
+ QItemSelectionModel::Rows | QItemSelectionModel::Select);
+ // Called once per destination to ensure all results are in view, unless
+ // transactions are not ordered by (ascending or descending) date.
+ transactionView->scrollTo(targetIndex);
+ // scrollTo() does not scroll far enough the first time when transactions
+ // are ordered by ascending date.
+ if (index == results[0]) transactionView->scrollTo(targetIndex);
+ }
+}
+
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void TransactionView::resizeEvent(QResizeEvent* event)
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 82e929b53f..66dc5bc86b 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -7,6 +7,8 @@
#include <qt/guiutil.h>
+#include <uint256.h>
+
#include <QWidget>
#include <QKeyEvent>
@@ -116,7 +118,7 @@ public Q_SLOTS:
void changedSearch();
void exportClicked();
void focusTransaction(const QModelIndex&);
-
+ void focusTransaction(const uint256& txid);
};
#endif // BITCOIN_QT_TRANSACTIONVIEW_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 34954a6bfa..e7d9d276d7 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -14,8 +14,8 @@
#include <qt/sendcoinsdialog.h>
#include <qt/transactiontablemodel.h>
-#include <base58.h>
#include <chain.h>
+#include <key_io.h>
#include <keystore.h>
#include <validation.h>
#include <net.h> // for g_connman
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 9e13de79be..811996b98f 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -5,6 +5,11 @@
#ifndef BITCOIN_QT_WALLETMODEL_H
#define BITCOIN_QT_WALLETMODEL_H
+#include <amount.h>
+#include <key.h>
+#include <serialize.h>
+#include <script/standard.h>
+
#include <qt/paymentrequestplus.h>
#include <qt/walletmodeltransaction.h>
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 7eced9289d..64497a3431 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -68,6 +68,9 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex)));
connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo()));
+ // Highlight transaction after send
+ connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), transactionView, SLOT(focusTransaction(uint256)));
+
// Double-clicking on a transaction on the transaction history page shows details
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
@@ -91,6 +94,9 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
// Clicking on a transaction on the overview page simply sends you to transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage()));
+ // Navigate to transaction history page after send
+ connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), gui, SLOT(gotoHistoryPage()));
+
// Receive and report messages
connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));
diff --git a/src/rest.cpp b/src/rest.cpp
index eeeb3f5141..8cba59dbbc 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -90,7 +90,7 @@ static enum RetFormat ParseDataFormat(std::string& param, const std::string& str
static std::string AvailableDataFormatsString()
{
- std::string formats = "";
+ std::string formats;
for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
if (strlen(rf_names[i].name) > 0) {
formats.append(".");
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f1352a13cf..f2a1fd048f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -372,6 +372,9 @@ std::string EntryDescriptionString()
" \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
+ " ... ]\n"
+ " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n"
+ " \"transactionid\", (string) child transaction id\n"
" ... ]\n";
}
@@ -406,6 +409,15 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
}
info.pushKV("depends", depends);
+
+ UniValue spent(UniValue::VARR);
+ const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash());
+ const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it);
+ for (const CTxMemPool::txiter &childiter : setChildren) {
+ spent.push_back(childiter->GetTx().GetHash().ToString());
+ }
+
+ info.pushKV("spentby", spent);
}
UniValue mempoolToJSON(bool fVerbose)
@@ -1542,25 +1554,19 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
- bool havehash = !request.params[1].isNull();
- uint256 hash;
- if (havehash) {
- hash = uint256S(request.params[1].get_str());
- }
-
- {
+ if (request.params[1].isNull()) {
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();
+ pindex = chainActive.Tip();
+ } else {
+ uint256 hash = uint256S(request.params[1].get_str());
+ LOCK(cs_main);
+ 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");
}
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index a95ea0cf92..0eeb3f98b3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -43,6 +43,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbyaddress", 0, "minconf" },
{ "listreceivedbyaddress", 1, "include_empty" },
{ "listreceivedbyaddress", 2, "include_watchonly" },
+ { "listreceivedbyaddress", 3, "address_filter" },
{ "listreceivedbyaccount", 0, "minconf" },
{ "listreceivedbyaccount", 1, "include_empty" },
{ "listreceivedbyaccount", 2, "include_watchonly" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 3f3bfa0cfd..3073a49d0d 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -3,7 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
#include <amount.h>
#include <chain.h>
#include <chainparams.h>
@@ -13,6 +12,7 @@
#include <core_io.h>
#include <init.h>
#include <validation.h>
+#include <key_io.h>
#include <miner.h>
#include <net.h>
#include <policy/fees.h>
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index f573c7dbeb..49e865a64a 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
#include <chain.h>
#include <clientversion.h>
#include <core_io.h>
#include <crypto/ripemd160.h>
#include <init.h>
+#include <key_io.h>
#include <validation.h>
#include <httpserver.h>
#include <net.h>
@@ -224,13 +224,10 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request)
std::string strPrivkey = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strPrivkey);
- if (!fGood)
+ CKey key = DecodeSecret(strPrivkey);
+ if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
- CKey key = vchSecret.GetKey();
- if (!key.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 7a0225ff0d..fee2b765ba 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -89,7 +89,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
" \"pingtime\": n, (numeric) ping time (if available)\n"
" \"minping\": n, (numeric) minimum observed ping time (if any at all)\n"
" \"pingwait\": n, (numeric) ping wait (if non-zero)\n"
- " \"version\": v, (numeric) The peer version, such as 7001\n"
+ " \"version\": v, (numeric) The peer version, such as 70001\n"
" \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n"
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
" \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 813afde4db..8dcfb48e9a 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -3,7 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
#include <chain.h>
#include <coins.h>
#include <consensus/validation.h>
@@ -12,6 +11,7 @@
#include <keystore.h>
#include <validation.h>
#include <validationinterface.h>
+#include <key_io.h>
#include <merkleblock.h>
#include <net.h>
#include <policy/policy.h>
@@ -896,13 +896,9 @@ UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
const UniValue& keys = request.params[1].get_array();
for (unsigned int idx = 0; idx < keys.size(); ++idx) {
UniValue k = keys[idx];
- CBitcoinSecret vchSecret;
- if (!vchSecret.SetString(k.get_str())) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
- }
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(k.get_str());
if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
keystore.AddKey(key);
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index e5b4f6ca77..35401bf876 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -5,9 +5,9 @@
#include <rpc/server.h>
-#include <base58.h>
#include <fs.h>
#include <init.h>
+#include <key_io.h>
#include <random.h>
#include <sync.h>
#include <ui_interface.h>
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index cdcb68d15f..593962e710 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
+#include <key_io.h>
#include <keystore.h>
#include <pubkey.h>
#include <rpc/protocol.h>
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 2cdff7ee57..927b0267ca 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -226,23 +226,25 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co
}
bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
+ // Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal
+ assert(0 <= opcode && opcode <= OP_PUSHDATA4);
if (data.size() == 0) {
- // Could have used OP_0.
+ // Should have used OP_0.
return opcode == OP_0;
} else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) {
- // Could have used OP_1 .. OP_16.
- return opcode == OP_1 + (data[0] - 1);
+ // Should have used OP_1 .. OP_16.
+ return false;
} else if (data.size() == 1 && data[0] == 0x81) {
- // Could have used OP_1NEGATE.
- return opcode == OP_1NEGATE;
+ // Should have used OP_1NEGATE.
+ return false;
} else if (data.size() <= 75) {
- // Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
+ // Must have used a direct push (opcode indicating number of bytes pushed + those bytes).
return opcode == data.size();
} else if (data.size() <= 255) {
- // Could have used OP_PUSHDATA.
+ // Must have used OP_PUSHDATA.
return opcode == OP_PUSHDATA1;
} else if (data.size() <= 65535) {
- // Could have used OP_PUSHDATA2.
+ // Must have used OP_PUSHDATA2.
return opcode == OP_PUSHDATA2;
}
return true;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 838e502a0a..aaba5e5926 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -140,10 +140,9 @@ static CScript PushAll(const std::vector<valtype>& values)
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
- CScript script = fromPubKey;
std::vector<valtype> result;
txnouttype whichType;
- bool solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE);
+ bool solved = SignStep(creator, fromPubKey, result, whichType, SIGVERSION_BASE);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
@@ -153,8 +152,8 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
// Solver returns the subscript that needs to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
- script = subscript = CScript(result[0].begin(), result[0].end());
- solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH;
+ subscript = CScript(result[0].begin(), result[0].end());
+ solved = solved && SignStep(creator, subscript, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index b338d6d366..0d8bd90119 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -52,6 +52,17 @@ public:
{
CAddrMan::Delete(nId);
}
+
+ // Simulates connection failure so that we can test eviction of offline nodes
+ void SimConnFail(CService& addr)
+ {
+ int64_t nLastSuccess = 1;
+ Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
+
+ bool count_failure = false;
+ int64_t nLastTry = GetAdjustedTime()-61;
+ Attempt(addr, count_failure, nLastTry);
+ }
};
static CNetAddr ResolveIP(const char* ip)
@@ -226,7 +237,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
BOOST_CHECK_EQUAL(addrman.size(), 0);
for (unsigned int i = 1; i < 18; i++) {
- CService addr = ResolveService("250.1.1." + boost::to_string(i));
+ CService addr = ResolveService("250.1.1." + std::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
//Test: No collision in new table yet.
@@ -252,7 +263,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_CHECK_EQUAL(addrman.size(), 0);
for (unsigned int i = 1; i < 80; i++) {
- CService addr = ResolveService("250.1.1." + boost::to_string(i));
+ CService addr = ResolveService("250.1.1." + std::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
addrman.Good(CAddress(addr, NODE_NONE));
@@ -385,7 +396,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
for (unsigned int i = 1; i < (8 * 256); i++) {
int octet1 = i % 256;
int octet2 = i >> 8 % 256;
- std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + ".1.23";
+ std::string strAddr = std::to_string(octet1) + "." + std::to_string(octet2) + ".1.23";
CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE);
// Ensure that for all addrs in addrman, isTerrible == false.
@@ -436,8 +447,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
CAddrInfo infoi = CAddrInfo(
- CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE),
- ResolveIP("250.1.1." + boost::to_string(i)));
+ CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
+ ResolveIP("250.1.1." + std::to_string(i)));
int bucket = infoi.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
@@ -448,8 +459,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
buckets.clear();
for (int j = 0; j < 255; j++) {
CAddrInfo infoj = CAddrInfo(
- CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE),
- ResolveIP("250." + boost::to_string(j) + ".1.1"));
+ CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE),
+ ResolveIP("250." + std::to_string(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
@@ -488,8 +499,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
CAddrInfo infoi = CAddrInfo(
- CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE),
- ResolveIP("250.1.1." + boost::to_string(i)));
+ CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
+ ResolveIP("250.1.1." + std::to_string(i)));
int bucket = infoi.GetNewBucket(nKey1);
buckets.insert(bucket);
}
@@ -501,7 +512,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
for (int j = 0; j < 4 * 255; j++) {
CAddrInfo infoj = CAddrInfo(CAddress(
ResolveService(
- boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE),
+ std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE),
ResolveIP("251.4.1.1"));
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
@@ -514,7 +525,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
for (int p = 0; p < 255; p++) {
CAddrInfo infoj = CAddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
- ResolveIP("250." + boost::to_string(p) + ".1.1"));
+ ResolveIP("250." + std::to_string(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
}
@@ -522,4 +533,158 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
// than 64 buckets.
BOOST_CHECK(buckets.size() > 64);
}
+
+
+BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
+{
+ CAddrManTest addrman;
+
+ // Set addrman addr placement to be deterministic.
+ addrman.MakeDeterministic();
+
+ BOOST_CHECK(addrman.size() == 0);
+
+ // Empty addrman should return blank addrman info.
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+
+ // Add twenty two addresses.
+ CNetAddr source = ResolveIP("252.2.2.2");
+ for (unsigned int i = 1; i < 23; i++) {
+ CService addr = ResolveService("250.1.1."+std::to_string(i));
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ // No collisions yet.
+ BOOST_CHECK(addrman.size() == i);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ }
+
+ // Ensure Good handles duplicates well.
+ for (unsigned int i = 1; i < 23; i++) {
+ CService addr = ResolveService("250.1.1."+std::to_string(i));
+ addrman.Good(addr);
+
+ BOOST_CHECK(addrman.size() == 22);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ }
+
+}
+
+BOOST_AUTO_TEST_CASE(addrman_noevict)
+{
+ CAddrManTest addrman;
+
+ // Set addrman addr placement to be deterministic.
+ addrman.MakeDeterministic();
+
+ // Add twenty two addresses.
+ CNetAddr source = ResolveIP("252.2.2.2");
+ for (unsigned int i = 1; i < 23; i++) {
+ CService addr = ResolveService("250.1.1."+std::to_string(i));
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ // No collision yet.
+ BOOST_CHECK(addrman.size() == i);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ }
+
+ // Collision between 23 and 19.
+ CService addr23 = ResolveService("250.1.1.23");
+ addrman.Add(CAddress(addr23, NODE_NONE), source);
+ addrman.Good(addr23);
+
+ BOOST_CHECK(addrman.size() == 23);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.19:0");
+
+ // 23 should be discarded and 19 not evicted.
+ addrman.ResolveCollisions();
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+
+ // Lets create two collisions.
+ for (unsigned int i = 24; i < 33; i++) {
+ CService addr = ResolveService("250.1.1."+std::to_string(i));
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ BOOST_CHECK(addrman.size() == i);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ }
+
+ // Cause a collision.
+ CService addr33 = ResolveService("250.1.1.33");
+ addrman.Add(CAddress(addr33, NODE_NONE), source);
+ addrman.Good(addr33);
+ BOOST_CHECK(addrman.size() == 33);
+
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.27:0");
+
+ // Cause a second collision.
+ addrman.Add(CAddress(addr23, NODE_NONE), source);
+ addrman.Good(addr23);
+ BOOST_CHECK(addrman.size() == 33);
+
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() != "[::]:0");
+ addrman.ResolveCollisions();
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+}
+
+BOOST_AUTO_TEST_CASE(addrman_evictionworks)
+{
+ CAddrManTest addrman;
+
+ // Set addrman addr placement to be deterministic.
+ addrman.MakeDeterministic();
+
+ BOOST_CHECK(addrman.size() == 0);
+
+ // Empty addrman should return blank addrman info.
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+
+ // Add twenty two addresses.
+ CNetAddr source = ResolveIP("252.2.2.2");
+ for (unsigned int i = 1; i < 23; i++) {
+ CService addr = ResolveService("250.1.1."+std::to_string(i));
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ // No collision yet.
+ BOOST_CHECK(addrman.size() == i);
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ }
+
+ // Collision between 23 and 19.
+ CService addr = ResolveService("250.1.1.23");
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ BOOST_CHECK(addrman.size() == 23);
+ CAddrInfo info = addrman.SelectTriedCollision();
+ BOOST_CHECK(info.ToString() == "250.1.1.19:0");
+
+ // Ensure test of address fails, so that it is evicted.
+ addrman.SimConnFail(info);
+
+ // Should swap 23 for 19.
+ addrman.ResolveCollisions();
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+
+ // If 23 was swapped for 19, then this should cause no collisions.
+ addrman.Add(CAddress(addr, NODE_NONE), source);
+ addrman.Good(addr);
+
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+
+ // If we insert 19 is should collide with 23.
+ CService addr19 = ResolveService("250.1.1.19");
+ addrman.Add(CAddress(addr19, NODE_NONE), source);
+ addrman.Good(addr19);
+
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.23:0");
+
+ addrman.ResolveCollisions();
+ BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index b9ac62a437..1210c7a7ee 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{
std::string strEnc = EncodeBase32(vstrIn[i]);
- BOOST_CHECK(strEnc == vstrOut[i]);
+ BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
std::string strDec = DecodeBase32(vstrOut[i]);
- BOOST_CHECK(strDec == vstrIn[i]);
+ BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
}
}
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index a2d4f82695..f90d4f90cb 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -2,17 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
-
#include <test/data/base58_encode_decode.json.h>
-#include <test/data/base58_keys_invalid.json.h>
-#include <test/data/base58_keys_valid.json.h>
-#include <key.h>
-#include <script/script.h>
+#include <base58.h>
#include <test/test_bitcoin.h>
-#include <uint256.h>
-#include <util.h>
#include <utilstrencodings.h>
#include <univalue.h>
@@ -73,135 +66,4 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
-// Goal: check that parsed keys match test payload
-BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
-{
- UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
- CBitcoinSecret secret;
- CTxDestination destination;
- SelectParams(CBaseChainParams::MAIN);
-
- for (unsigned int idx = 0; idx < tests.size(); idx++) {
- UniValue test = tests[idx];
- std::string strTest = test.write();
- if (test.size() < 3) { // Allow for extra stuff (useful for comments)
- BOOST_ERROR("Bad test: " << strTest);
- continue;
- }
- std::string exp_base58string = test[0].get_str();
- std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
- const UniValue &metadata = test[2].get_obj();
- bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
- SelectParams(find_value(metadata, "chain").get_str());
- bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
- if (isPrivkey) {
- bool isCompressed = find_value(metadata, "isCompressed").get_bool();
- // Must be valid private key
- BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
- BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
- CKey privkey = secret.GetKey();
- BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
- BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
-
- // Private key must be invalid public key
- destination = DecodeDestination(exp_base58string);
- BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
- } else {
- // Must be valid public key
- destination = DecodeDestination(exp_base58string);
- CScript script = GetScriptForDestination(destination);
- BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
- BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
-
- // Try flipped case version
- for (char& c : exp_base58string) {
- if (c >= 'a' && c <= 'z') {
- c = (c - 'a') + 'A';
- } else if (c >= 'A' && c <= 'Z') {
- c = (c - 'A') + 'a';
- }
- }
- destination = DecodeDestination(exp_base58string);
- BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
- if (IsValidDestination(destination)) {
- script = GetScriptForDestination(destination);
- BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
- }
-
- // Public key must be invalid private key
- secret.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest);
- }
- }
-}
-
-// Goal: check that generated keys match test vectors
-BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
-{
- UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
-
- for (unsigned int idx = 0; idx < tests.size(); idx++) {
- UniValue test = tests[idx];
- std::string strTest = test.write();
- if (test.size() < 3) // Allow for extra stuff (useful for comments)
- {
- BOOST_ERROR("Bad test: " << strTest);
- continue;
- }
- std::string exp_base58string = test[0].get_str();
- std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
- const UniValue &metadata = test[2].get_obj();
- bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
- SelectParams(find_value(metadata, "chain").get_str());
- if (isPrivkey) {
- bool isCompressed = find_value(metadata, "isCompressed").get_bool();
- CKey key;
- key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
- assert(key.IsValid());
- CBitcoinSecret secret;
- secret.SetKey(key);
- BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
- } else {
- CTxDestination dest;
- CScript exp_script(exp_payload.begin(), exp_payload.end());
- ExtractDestination(exp_script, dest);
- std::string address = EncodeDestination(dest);
-
- BOOST_CHECK_EQUAL(address, exp_base58string);
- }
- }
-
- SelectParams(CBaseChainParams::MAIN);
-}
-
-
-// Goal: check that base58 parsing code is robust against a variety of corrupted data
-BOOST_AUTO_TEST_CASE(base58_keys_invalid)
-{
- UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
- CBitcoinSecret secret;
- CTxDestination destination;
-
- for (unsigned int idx = 0; idx < tests.size(); idx++) {
- UniValue test = tests[idx];
- std::string strTest = test.write();
- if (test.size() < 1) // Allow for extra stuff (useful for comments)
- {
- BOOST_ERROR("Bad test: " << strTest);
- continue;
- }
- std::string exp_base58string = test[0].get_str();
-
- // must be invalid as public and as private key
- for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
- SelectParams(chain);
- destination = DecodeDestination(exp_base58string);
- BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
- secret.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest);
- }
- }
-}
-
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index b31f73f3b5..f785cede81 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{
std::string strEnc = EncodeBase64(vstrIn[i]);
- BOOST_CHECK(strEnc == vstrOut[i]);
+ BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
std::string strDec = DecodeBase64(strEnc);
- BOOST_CHECK(strDec == vstrIn[i]);
+ BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
}
}
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 438ddc177d..3c9ff1877d 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -4,8 +4,8 @@
#include <boost/test/unit_test.hpp>
-#include <base58.h>
#include <key.h>
+#include <key_io.h>
#include <uint256.h>
#include <util.h>
#include <utilstrencodings.h>
@@ -99,20 +99,12 @@ void RunTest(const TestVector &test) {
pubkey.Encode(data);
// Test private key
- CBitcoinExtKey b58key; b58key.SetKey(key);
- BOOST_CHECK(b58key.ToString() == derive.prv);
-
- CBitcoinExtKey b58keyDecodeCheck(derive.prv);
- CExtKey checkKey = b58keyDecodeCheck.GetKey();
- assert(checkKey == key); //ensure a base58 decoded key also matches
+ BOOST_CHECK(EncodeExtKey(key) == derive.prv);
+ BOOST_CHECK(DecodeExtKey(derive.prv) == key); //ensure a base58 decoded key also matches
// Test public key
- CBitcoinExtPubKey b58pubkey; b58pubkey.SetKey(pubkey);
- BOOST_CHECK(b58pubkey.ToString() == derive.pub);
-
- CBitcoinExtPubKey b58PubkeyDecodeCheck(derive.pub);
- CExtPubKey checkPubKey = b58PubkeyDecodeCheck.GetKey();
- assert(checkPubKey == pubkey); //ensure a base58 decoded pubkey also matches
+ BOOST_CHECK(EncodeExtPubKey(pubkey) == derive.pub);
+ BOOST_CHECK(DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches
// Derive new keys
CExtKey keyNew;
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index af5533b109..73c8eb5168 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -4,9 +4,9 @@
#include <bloom.h>
-#include <base58.h>
#include <clientversion.h>
#include <key.h>
+#include <key_io.h>
#include <merkleblock.h>
#include <primitives/block.h>
#include <random.h>
@@ -85,10 +85,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
{
std::string strSecret = std::string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C");
- CBitcoinSecret vchSecret;
- BOOST_CHECK(vchSecret.SetString(strSecret));
-
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(strSecret);
CPubKey pubkey = key.GetPubKey();
std::vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end());
diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/key_io_invalid.json
index 2056c7491c..2056c7491c 100644
--- a/src/test/data/base58_keys_invalid.json
+++ b/src/test/data/key_io_invalid.json
diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/key_io_valid.json
index 8418a6002d..8418a6002d 100644
--- a/src/test/data/base58_keys_valid.json
+++ b/src/test/data/key_io_valid.json
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 7e39ec7599..0bcecc58fe 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -516,5 +516,9 @@
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."],
+[[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]],
+"0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "P2SH,WITNESS"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
new file mode 100644
index 0000000000..1ac1e0015b
--- /dev/null
+++ b/src/test/key_io_tests.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2011-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/data/key_io_invalid.json.h>
+#include <test/data/key_io_valid.json.h>
+
+#include <key.h>
+#include <key_io.h>
+#include <script/script.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <univalue.h>
+
+extern UniValue read_json(const std::string& jsondata);
+
+BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup)
+
+// Goal: check that parsed keys match test payload
+BOOST_AUTO_TEST_CASE(key_io_valid_parse)
+{
+ UniValue tests = read_json(std::string(json_tests::key_io_valid, json_tests::key_io_valid + sizeof(json_tests::key_io_valid)));
+ CKey privkey;
+ CTxDestination destination;
+ SelectParams(CBaseChainParams::MAIN);
+
+ for (unsigned int idx = 0; idx < tests.size(); idx++) {
+ UniValue test = tests[idx];
+ std::string strTest = test.write();
+ if (test.size() < 3) { // Allow for extra stuff (useful for comments)
+ BOOST_ERROR("Bad test: " << strTest);
+ continue;
+ }
+ std::string exp_base58string = test[0].get_str();
+ std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
+ const UniValue &metadata = test[2].get_obj();
+ bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
+ SelectParams(find_value(metadata, "chain").get_str());
+ bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
+ if (isPrivkey) {
+ bool isCompressed = find_value(metadata, "isCompressed").get_bool();
+ // Must be valid private key
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
+ BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
+ BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
+
+ // Private key must be invalid public key
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
+ } else {
+ // Must be valid public key
+ destination = DecodeDestination(exp_base58string);
+ CScript script = GetScriptForDestination(destination);
+ BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
+ BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
+
+ // Try flipped case version
+ for (char& c : exp_base58string) {
+ if (c >= 'a' && c <= 'z') {
+ c = (c - 'a') + 'A';
+ } else if (c >= 'A' && c <= 'Z') {
+ c = (c - 'A') + 'a';
+ }
+ }
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
+ if (IsValidDestination(destination)) {
+ script = GetScriptForDestination(destination);
+ BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
+ }
+
+ // Public key must be invalid private key
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest);
+ }
+ }
+}
+
+// Goal: check that generated keys match test vectors
+BOOST_AUTO_TEST_CASE(key_io_valid_gen)
+{
+ UniValue tests = read_json(std::string(json_tests::key_io_valid, json_tests::key_io_valid + sizeof(json_tests::key_io_valid)));
+
+ for (unsigned int idx = 0; idx < tests.size(); idx++) {
+ UniValue test = tests[idx];
+ std::string strTest = test.write();
+ if (test.size() < 3) // Allow for extra stuff (useful for comments)
+ {
+ BOOST_ERROR("Bad test: " << strTest);
+ continue;
+ }
+ std::string exp_base58string = test[0].get_str();
+ std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
+ const UniValue &metadata = test[2].get_obj();
+ bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
+ SelectParams(find_value(metadata, "chain").get_str());
+ if (isPrivkey) {
+ bool isCompressed = find_value(metadata, "isCompressed").get_bool();
+ CKey key;
+ key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
+ assert(key.IsValid());
+ BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest);
+ } else {
+ CTxDestination dest;
+ CScript exp_script(exp_payload.begin(), exp_payload.end());
+ ExtractDestination(exp_script, dest);
+ std::string address = EncodeDestination(dest);
+
+ BOOST_CHECK_EQUAL(address, exp_base58string);
+ }
+ }
+
+ SelectParams(CBaseChainParams::MAIN);
+}
+
+
+// Goal: check that base58 parsing code is robust against a variety of corrupted data
+BOOST_AUTO_TEST_CASE(key_io_invalid)
+{
+ UniValue tests = read_json(std::string(json_tests::key_io_invalid, json_tests::key_io_invalid + sizeof(json_tests::key_io_invalid))); // Negative testcases
+ CKey privkey;
+ CTxDestination destination;
+
+ for (unsigned int idx = 0; idx < tests.size(); idx++) {
+ UniValue test = tests[idx];
+ std::string strTest = test.write();
+ if (test.size() < 1) // Allow for extra stuff (useful for comments)
+ {
+ BOOST_ERROR("Bad test: " << strTest);
+ continue;
+ }
+ std::string exp_base58string = test[0].get_str();
+
+ // must be invalid as public and as private key
+ for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
+ SelectParams(chain);
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey in mainnet:" + strTest);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 55ee1ecf6b..64c57f0705 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -4,7 +4,7 @@
#include <key.h>
-#include <base58.h>
+#include <key_io.h>
#include <script/script.h>
#include <uint256.h>
#include <util.h>
@@ -32,21 +32,16 @@ BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(key_test1)
{
- CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1;
- BOOST_CHECK( bsecret1.SetString (strSecret1));
- BOOST_CHECK( bsecret2.SetString (strSecret2));
- BOOST_CHECK( bsecret1C.SetString(strSecret1C));
- BOOST_CHECK( bsecret2C.SetString(strSecret2C));
- BOOST_CHECK(!baddress1.SetString(strAddressBad));
-
- CKey key1 = bsecret1.GetKey();
- BOOST_CHECK(key1.IsCompressed() == false);
- CKey key2 = bsecret2.GetKey();
- BOOST_CHECK(key2.IsCompressed() == false);
- CKey key1C = bsecret1C.GetKey();
- BOOST_CHECK(key1C.IsCompressed() == true);
- CKey key2C = bsecret2C.GetKey();
- BOOST_CHECK(key2C.IsCompressed() == true);
+ CKey key1 = DecodeSecret(strSecret1);
+ BOOST_CHECK(key1.IsValid() && !key1.IsCompressed());
+ CKey key2 = DecodeSecret(strSecret2);
+ BOOST_CHECK(key2.IsValid() && !key2.IsCompressed());
+ CKey key1C = DecodeSecret(strSecret1C);
+ BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed());
+ CKey key2C = DecodeSecret(strSecret2C);
+ BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed());
+ CKey bad_key = DecodeSecret(strAddressBad);
+ BOOST_CHECK(!bad_key.IsValid());
CPubKey pubkey1 = key1. GetPubKey();
CPubKey pubkey2 = key2. GetPubKey();
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index ca57f58905..e03234060d 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
ipv4Addr.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
- std::string pszDest = "";
+ std::string pszDest;
bool fInboundIn = false;
// Test that fFeeler is false by default.
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index db9162c0db..01c3a6cedd 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
test.erase(InsecureRandRange(test.size()));
}
if (InsecureRandBits(3) == 2) {
- int new_size = std::max<int>(0, std::min<int>(30, test.size() + (InsecureRandRange(5)) - 2));
+ int new_size = std::max(0, std::min(30, (int)test.size() + (int)InsecureRandRange(5) - 2));
test.resize(new_size);
}
if (InsecureRandBits(3) == 3) {
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 108c1a063e..892e4f2dac 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -5,8 +5,8 @@
#include <rpc/server.h>
#include <rpc/client.h>
-#include <base58.h>
#include <core_io.h>
+#include <key_io.h>
#include <netbase.h>
#include <test/test_bitcoin.h>
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 760f933abc..179df7dd38 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -56,8 +56,8 @@ BOOST_AUTO_TEST_CASE(manythreads)
int counter[10] = { 0 };
FastRandomContext rng(42);
auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9]
- auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + rc.randrange(1012); }; // [-11, 1000]
- auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + rc.randrange(2001); }; // [-1000, 1000]
+ auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
+ auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]
boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now();
boost::chrono::system_clock::time_point now = start;
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 463bed5957..84b61bea86 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -82,6 +82,20 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
"04 67 8a fd b0");
BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected + sizeof(ParseHex_expected),
+ ParseHex_expected + sizeof(ParseHex_expected)),
+ "");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected + sizeof(ParseHex_expected),
+ ParseHex_expected + sizeof(ParseHex_expected), true),
+ "");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected, ParseHex_expected),
+ "");
+
+ BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected, ParseHex_expected, true),
"");
@@ -90,6 +104,58 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
BOOST_CHECK_EQUAL(
HexStr(ParseHex_vec, true),
"04 67 8a fd b0");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()),
+ "b0fd8a6704"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true),
+ "b0 fd 8a 67 04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ ""
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ ""
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ "04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "b0fd8a6704"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ "b0 fd 8a 67 04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704"
+ );
}
@@ -98,10 +164,27 @@ BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat)
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17");
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z");
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000");
}
+BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
+{
+ BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
+}
+
+BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
+{
+ BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
+}
+
+BOOST_AUTO_TEST_CASE(util_FormatISO8601Time)
+{
+ BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z");
+}
+
class TestArgsManager : public ArgsManager
{
public:
@@ -688,7 +771,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
thr.join();
BOOST_CHECK_EQUAL(threadresult, true);
#ifndef WIN32
- // Try to aquire lock in child process while we're holding it, this should fail.
+ // Try to acquire lock in child process while we're holding it, this should fail.
char ch;
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
@@ -699,7 +782,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
// Probing lock from our side now should succeed, but not hold on to the lock.
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
- // Try to acquire the lock in the child process, this should be succesful.
+ // Try to acquire the lock in the child process, this should be successful.
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
BOOST_CHECK_EQUAL((bool)ch, true);
diff --git a/src/util.cpp b/src/util.cpp
index dcf7ed38b1..62cdce3012 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -79,6 +79,7 @@
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
+#include <thread>
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
@@ -314,12 +315,14 @@ static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fSt
if (*fStartedNewLine) {
int64_t nTimeMicros = GetTimeMicros();
- strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTimeMicros/1000000);
- if (fLogTimeMicros)
- strStamped += strprintf(".%06d", nTimeMicros%1000000);
+ strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
+ if (fLogTimeMicros) {
+ strStamped.pop_back();
+ strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
+ }
int64_t mocktime = GetMockTime();
if (mocktime) {
- strStamped += " (mocktime: " + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", mocktime) + ")";
+ strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
}
strStamped += ' ' + str;
} else
@@ -927,11 +930,7 @@ bool SetupNetworking()
int GetNumCores()
{
-#if BOOST_VERSION >= 105600
- return boost::thread::physical_concurrency();
-#else // Must fall back to hardware_concurrency, which unfortunately counts virtual cores
- return boost::thread::hardware_concurrency();
-#endif
+ return std::thread::hardware_concurrency();
}
std::string CopyrightHolders(const std::string& strPrefix)
diff --git a/src/util.h b/src/util.h
index 9490a5678f..e4170d8aa2 100644
--- a/src/util.h
+++ b/src/util.h
@@ -312,9 +312,8 @@ std::string HelpMessageGroup(const std::string& message);
std::string HelpMessageOpt(const std::string& option, const std::string& message);
/**
- * Return the number of physical cores available on the current system.
- * @note This does not count virtual cores, such as those provided by HyperThreading
- * when boost is newer than 1.56.
+ * Return the number of cores available on the current system.
+ * @note This does count virtual cores, such as those provided by HyperThreading.
*/
int GetNumCores();
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index 52158e9804..d1025fc7bf 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -127,46 +127,11 @@ std::string EncodeBase64(const unsigned char* pch, size_t len)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- std::string strRet = "";
- strRet.reserve((len+2)/3*4);
-
- int mode=0, left=0;
- const unsigned char *pchEnd = pch+len;
-
- while (pch<pchEnd)
- {
- int enc = *(pch++);
- switch (mode)
- {
- case 0: // we have no bits
- strRet += pbase64[enc >> 2];
- left = (enc & 3) << 4;
- mode = 1;
- break;
-
- case 1: // we have two bits
- strRet += pbase64[left | (enc >> 4)];
- left = (enc & 15) << 2;
- mode = 2;
- break;
-
- case 2: // we have four bits
- strRet += pbase64[left | (enc >> 6)];
- strRet += pbase64[enc & 63];
- mode = 0;
- break;
- }
- }
-
- if (mode)
- {
- strRet += pbase64[left];
- strRet += '=';
- if (mode == 1)
- strRet += '=';
- }
-
- return strRet;
+ std::string str;
+ str.reserve(((len + 2) / 3) * 4);
+ ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
+ while (str.size() % 4) str += '=';
+ return str;
}
std::string EncodeBase64(const std::string& str)
@@ -193,68 +158,32 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
- if (pfInvalid)
- *pfInvalid = false;
-
- std::vector<unsigned char> vchRet;
- vchRet.reserve(strlen(p)*3/4);
-
- int mode = 0;
- int left = 0;
-
- while (1)
- {
- int dec = decode64_table[(unsigned char)*p];
- if (dec == -1) break;
- p++;
- switch (mode)
- {
- case 0: // we have no bits and get 6
- left = dec;
- mode = 1;
- break;
-
- case 1: // we have 6 bits and keep 4
- vchRet.push_back((left<<2) | (dec>>4));
- left = dec & 15;
- mode = 2;
- break;
-
- case 2: // we have 4 bits and get 6, we keep 2
- vchRet.push_back((left<<4) | (dec>>2));
- left = dec & 3;
- mode = 3;
- break;
-
- case 3: // we have 2 bits and get 6
- vchRet.push_back((left<<6) | dec);
- mode = 0;
- break;
- }
+ const char* e = p;
+ std::vector<uint8_t> val;
+ val.reserve(strlen(p));
+ while (*p != 0) {
+ int x = decode64_table[(unsigned char)*p];
+ if (x == -1) break;
+ val.push_back(x);
+ ++p;
}
- if (pfInvalid)
- switch (mode)
- {
- case 0: // 4n base64 characters processed: ok
- break;
-
- case 1: // 4n+1 base64 character processed: impossible
- *pfInvalid = true;
- break;
-
- case 2: // 4n+2 base64 characters processed: require '=='
- if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
- *pfInvalid = true;
- break;
-
- case 3: // 4n+3 base64 characters processed: require '='
- if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
- *pfInvalid = true;
- break;
+ std::vector<unsigned char> ret;
+ ret.reserve((val.size() * 3) / 4);
+ bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
+
+ const char* q = p;
+ while (valid && *p != 0) {
+ if (*p != '=') {
+ valid = false;
+ break;
}
+ ++p;
+ }
+ valid = valid && (p - e) % 4 == 0 && p - q < 4;
+ if (pfInvalid) *pfInvalid = !valid;
- return vchRet;
+ return ret;
}
std::string DecodeBase64(const std::string& str)
@@ -267,59 +196,11 @@ std::string EncodeBase32(const unsigned char* pch, size_t len)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
- std::string strRet="";
- strRet.reserve((len+4)/5*8);
-
- int mode=0, left=0;
- const unsigned char *pchEnd = pch+len;
-
- while (pch<pchEnd)
- {
- int enc = *(pch++);
- switch (mode)
- {
- case 0: // we have no bits
- strRet += pbase32[enc >> 3];
- left = (enc & 7) << 2;
- mode = 1;
- break;
-
- case 1: // we have three bits
- strRet += pbase32[left | (enc >> 6)];
- strRet += pbase32[(enc >> 1) & 31];
- left = (enc & 1) << 4;
- mode = 2;
- break;
-
- case 2: // we have one bit
- strRet += pbase32[left | (enc >> 4)];
- left = (enc & 15) << 1;
- mode = 3;
- break;
-
- case 3: // we have four bits
- strRet += pbase32[left | (enc >> 7)];
- strRet += pbase32[(enc >> 2) & 31];
- left = (enc & 3) << 3;
- mode = 4;
- break;
-
- case 4: // we have two bits
- strRet += pbase32[left | (enc >> 5)];
- strRet += pbase32[enc & 31];
- mode = 0;
- }
- }
-
- static const int nPadding[5] = {0, 6, 4, 3, 1};
- if (mode)
- {
- strRet += pbase32[left];
- for (int n=0; n<nPadding[mode]; n++)
- strRet += '=';
- }
-
- return strRet;
+ std::string str;
+ str.reserve(((len + 4) / 5) * 8);
+ ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len);
+ while (str.size() % 8) str += '=';
+ return str;
}
std::string EncodeBase32(const std::string& str)
@@ -346,102 +227,32 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
- if (pfInvalid)
- *pfInvalid = false;
-
- std::vector<unsigned char> vchRet;
- vchRet.reserve((strlen(p))*5/8);
-
- int mode = 0;
- int left = 0;
-
- while (1)
- {
- int dec = decode32_table[(unsigned char)*p];
- if (dec == -1) break;
- p++;
- switch (mode)
- {
- case 0: // we have no bits and get 5
- left = dec;
- mode = 1;
- break;
-
- case 1: // we have 5 bits and keep 2
- vchRet.push_back((left<<3) | (dec>>2));
- left = dec & 3;
- mode = 2;
- break;
-
- case 2: // we have 2 bits and keep 7
- left = left << 5 | dec;
- mode = 3;
- break;
-
- case 3: // we have 7 bits and keep 4
- vchRet.push_back((left<<1) | (dec>>4));
- left = dec & 15;
- mode = 4;
- break;
-
- case 4: // we have 4 bits, and keep 1
- vchRet.push_back((left<<4) | (dec>>1));
- left = dec & 1;
- mode = 5;
- break;
-
- case 5: // we have 1 bit, and keep 6
- left = left << 5 | dec;
- mode = 6;
- break;
-
- case 6: // we have 6 bits, and keep 3
- vchRet.push_back((left<<2) | (dec>>3));
- left = dec & 7;
- mode = 7;
- break;
-
- case 7: // we have 3 bits, and keep 0
- vchRet.push_back((left<<5) | dec);
- mode = 0;
- break;
- }
+ const char* e = p;
+ std::vector<uint8_t> val;
+ val.reserve(strlen(p));
+ while (*p != 0) {
+ int x = decode32_table[(unsigned char)*p];
+ if (x == -1) break;
+ val.push_back(x);
+ ++p;
}
- if (pfInvalid)
- switch (mode)
- {
- case 0: // 8n base32 characters processed: ok
- break;
-
- case 1: // 8n+1 base32 characters processed: impossible
- case 3: // +3
- case 6: // +6
- *pfInvalid = true;
- break;
-
- case 2: // 8n+2 base32 characters processed: require '======'
- if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1)
- *pfInvalid = true;
- break;
-
- case 4: // 8n+4 base32 characters processed: require '===='
- if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1)
- *pfInvalid = true;
- break;
-
- case 5: // 8n+5 base32 characters processed: require '==='
- if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1)
- *pfInvalid = true;
- break;
-
- case 7: // 8n+7 base32 characters processed: require '='
- if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1)
- *pfInvalid = true;
- break;
+ std::vector<unsigned char> ret;
+ ret.reserve((val.size() * 5) / 8);
+ bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
+
+ const char* q = p;
+ while (valid && *p != 0) {
+ if (*p != '=') {
+ valid = false;
+ break;
}
+ ++p;
+ }
+ valid = valid && (p - e) % 8 == 0 && p - q < 8;
+ if (pfInvalid) *pfInvalid = !valid;
- return vchRet;
+ return ret;
}
std::string DecodeBase32(const std::string& str)
diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h
index 994e6abbad..1c9cca90b2 100644
--- a/src/utilstrencodings.h
+++ b/src/utilstrencodings.h
@@ -151,7 +151,7 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
-bool ConvertBits(O& out, I it, I end) {
+bool ConvertBits(const O& outfn, I it, I end) {
size_t acc = 0;
size_t bits = 0;
constexpr size_t maxv = (1 << tobits) - 1;
@@ -161,12 +161,12 @@ bool ConvertBits(O& out, I it, I end) {
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
- out.push_back((acc >> bits) & maxv);
+ outfn((acc >> bits) & maxv);
}
++it;
}
if (pad) {
- if (bits) out.push_back((acc << (tobits - bits)) & maxv);
+ if (bits) outfn((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index e908173135..8a861039b3 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -85,3 +85,15 @@ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
ss << boost::posix_time::from_time_t(nTime);
return ss.str();
}
+
+std::string FormatISO8601DateTime(int64_t nTime) {
+ return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
+}
+
+std::string FormatISO8601Date(int64_t nTime) {
+ return DateTimeStrFormat("%Y-%m-%d", nTime);
+}
+
+std::string FormatISO8601Time(int64_t nTime) {
+ return DateTimeStrFormat("%H:%M:%SZ", nTime);
+}
diff --git a/src/utiltime.h b/src/utiltime.h
index 56cc31da67..807c52ffaf 100644
--- a/src/utiltime.h
+++ b/src/utiltime.h
@@ -27,6 +27,14 @@ void SetMockTime(int64_t nMockTimeIn);
int64_t GetMockTime();
void MilliSleep(int64_t n);
+/**
+ * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time}
+ * helper functions if possible.
+ */
std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime);
+std::string FormatISO8601DateTime(int64_t nTime);
+std::string FormatISO8601Date(int64_t nTime);
+std::string FormatISO8601Time(int64_t nTime);
+
#endif // BITCOIN_UTILTIME_H
diff --git a/src/validation.cpp b/src/validation.cpp
index d2438b0609..c677f44b3e 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1267,13 +1267,12 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
- log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S",
- pindexNew->GetBlockTime()));
+ log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
CBlockIndex *tip = chainActive.Tip();
assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__,
tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0),
- DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime()));
+ FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
}
@@ -1773,9 +1772,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
{
AssertLockHeld(cs_main);
assert(pindex);
- // pindex->phashBlock can be null if called by CreateNewBlock/TestBlockValidity
- assert((pindex->phashBlock == nullptr) ||
- (*pindex->phashBlock == block.GetHash()));
+ assert(*pindex->phashBlock == block.GetHash());
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
@@ -1849,8 +1846,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
// initial block download.
- bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash.
- !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
+ bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
// Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting
@@ -1859,12 +1855,65 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further
// duplicate transactions descending from the known pairs either.
// If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check.
+
+ // BIP34 requires that a block at height X (block X) has its coinbase
+ // scriptSig start with a CScriptNum of X (indicated height X). The above
+ // logic of no longer requiring BIP30 once BIP34 activates is flawed in the
+ // case that there is a block X before the BIP34 height of 227,931 which has
+ // an indicated height Y where Y is greater than X. The coinbase for block
+ // X would also be a valid coinbase for block Y, which could be a BIP30
+ // violation. An exhaustive search of all mainnet coinbases before the
+ // BIP34 height which have an indicated height greater than the block height
+ // reveals many occurrences. The 3 lowest indicated heights found are
+ // 209,921, 490,897, and 1,983,702 and thus coinbases for blocks at these 3
+ // heights would be the first opportunity for BIP30 to be violated.
+
+ // The search reveals a great many blocks which have an indicated height
+ // greater than 1,983,702, so we simply remove the optimization to skip
+ // BIP30 checking for blocks at height 1,983,702 or higher. Before we reach
+ // that block in another 25 years or so, we should take advantage of a
+ // future consensus change to do a new and improved version of BIP34 that
+ // will actually prevent ever creating any duplicate coinbases in the
+ // future.
+ static constexpr int BIP34_IMPLIES_BIP30_LIMIT = 1983702;
+
+ // There is no potential to create a duplicate coinbase at block 209,921
+ // because this is still before the BIP34 height and so explicit BIP30
+ // checking is still active.
+
+ // The final case is block 176,684 which has an indicated height of
+ // 490,897. Unfortunately, this issue was not discovered until about 2 weeks
+ // before block 490,897 so there was not much opportunity to address this
+ // case other than to carefully analyze it and determine it would not be a
+ // problem. Block 490,897 was, in fact, mined with a different coinbase than
+ // block 176,684, but it is important to note that even if it hadn't been or
+ // is remined on an alternate fork with a duplicate coinbase, we would still
+ // not run into a BIP30 violation. This is because the coinbase for 176,684
+ // is spent in block 185,956 in transaction
+ // d4f7fbbf92f4a3014a230b2dc70b8058d02eb36ac06b4a0736d9d60eaa9e8781. This
+ // spending transaction can't be duplicated because it also spends coinbase
+ // 0328dd85c331237f18e781d692c92de57649529bd5edf1d01036daea32ffde29. This
+ // coinbase has an indicated height of over 4.2 billion, and wouldn't be
+ // duplicatable until that height, and it's currently impossible to create a
+ // chain that long. Nevertheless we may wish to consider a future soft fork
+ // which retroactively prevents block 490,897 from creating a duplicate
+ // coinbase. The two historical BIP30 violations often provide a confusing
+ // edge case when manipulating the UTXO and it would be simpler not to have
+ // another edge case to deal with.
+
+ // testnet3 has no blocks before the BIP34 height with indicated heights
+ // post BIP34 before approximately height 486,000,000 and presumably will
+ // be reset before it reaches block 1,983,702 and starts doing unnecessary
+ // BIP30 checking again.
assert(pindex->pprev);
CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height);
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
- if (fEnforceBIP30) {
+ // TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a
+ // consensus change that ensures coinbases at those heights can not
+ // duplicate earlier coinbases.
+ if (fEnforceBIP30 || pindex->nHeight >= BIP34_IMPLIES_BIP30_LIMIT) {
for (const auto& tx : block.vtx) {
for (size_t o = 0; o < tx->vout.size(); o++) {
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
@@ -2179,7 +2228,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
- DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()),
+ FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
if (!warningMessages.empty())
LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", "));
@@ -3434,9 +3483,11 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == chainActive.Tip());
CCoinsViewCache viewNew(pcoinsTip.get());
+ uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
+ indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
@@ -3804,7 +3855,7 @@ bool LoadChainTip(const CChainParams& chainparams)
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
- DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
+ FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()),
GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
return true;
}
@@ -4502,7 +4553,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
std::string CBlockFileInfo::ToString() const
{
- return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
+ return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
}
CBlockFileInfo* GetBlockFileInfo(size_t n)
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 23c6279128..ebe7b48da0 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -52,20 +52,55 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
}
}
}
+
+CCriticalSection cs_db;
+std::map<std::string, CDBEnv> g_dbenvs; //!< Map from directory name to open db environment.
} // namespace
+CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+{
+ fs::path env_directory;
+ if (fs::is_regular_file(wallet_path)) {
+ // Special case for backwards compatibility: if wallet path points to an
+ // existing file, treat it as the path to a BDB data file in a parent
+ // directory that also contains BDB log files.
+ env_directory = wallet_path.parent_path();
+ database_filename = wallet_path.filename().string();
+ } else {
+ // Normal case: Interpret wallet path as a directory path containing
+ // data and log files.
+ env_directory = wallet_path;
+ database_filename = "wallet.dat";
+ }
+ LOCK(cs_db);
+ // Note: An ununsed temporary CDBEnv object may be created inside the
+ // emplace function if the key already exists. This is a little inefficient,
+ // but not a big concern since the map will be changed in the future to hold
+ // pointers instead of objects, anyway.
+ return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second;
+}
+
//
// CDB
//
-CDBEnv bitdb;
-
-void CDBEnv::EnvShutdown()
+void CDBEnv::Close()
{
if (!fDbEnvInit)
return;
fDbEnvInit = false;
+
+ for (auto& db : mapDb) {
+ auto count = mapFileUseCount.find(db.first);
+ assert(count == mapFileUseCount.end() || count->second == 0);
+ if (db.second) {
+ db.second->close(0);
+ delete db.second;
+ db.second = nullptr;
+ }
+ }
+
int ret = dbenv->close(0);
if (ret != 0)
LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
@@ -80,29 +115,25 @@ void CDBEnv::Reset()
fMockDb = false;
}
-CDBEnv::CDBEnv()
+CDBEnv::CDBEnv(const fs::path& dir_path) : strPath(dir_path.string())
{
Reset();
}
CDBEnv::~CDBEnv()
{
- EnvShutdown();
+ Close();
}
-void CDBEnv::Close()
-{
- EnvShutdown();
-}
-
-bool CDBEnv::Open(const fs::path& pathIn, bool retry)
+bool CDBEnv::Open(bool retry)
{
if (fDbEnvInit)
return true;
boost::this_thread::interruption_point();
- strPath = pathIn.string();
+ fs::path pathIn = strPath;
+ TryCreateDirectories(pathIn);
if (!LockDirectory(pathIn, ".walletlock")) {
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
return false;
@@ -150,7 +181,7 @@ bool CDBEnv::Open(const fs::path& pathIn, bool retry)
// failure is ok (well, not really, but it's not worse than what we started with)
}
// try opening it again one more time
- if (!Open(pathIn, false)) {
+ if (!Open(false /* retry */)) {
// if it still fails, it probably means we can't even create the database env
return false;
}
@@ -209,12 +240,15 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type
return RECOVER_FAIL;
// Try to recover:
- bool fRecovered = (*recoverFunc)(strFile, out_backup_filename);
+ bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
}
-bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
+bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
{
+ std::string filename;
+ CDBEnv* env = GetWalletEnv(file_path, filename);
+
// Recovery procedure:
// move wallet file to walletfilename.timestamp.bak
// Call Salvage with fAggressive=true to
@@ -225,7 +259,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
int64_t now = GetTime();
newFilename = strprintf("%s.%d.bak", filename, now);
- int result = bitdb.dbenv->dbrename(nullptr, filename.c_str(), nullptr,
+ int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
newFilename.c_str(), DB_AUTO_COMMIT);
if (result == 0)
LogPrintf("Renamed %s to %s\n", filename, newFilename);
@@ -236,7 +270,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
}
std::vector<CDBEnv::KeyValPair> salvagedData;
- bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData);
+ bool fSuccess = env->Salvage(newFilename, true, salvagedData);
if (salvagedData.empty())
{
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
@@ -244,7 +278,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
}
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(bitdb.dbenv.get(), 0);
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
filename.c_str(), // Filename
"main", // Logical db name
@@ -257,7 +291,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return false;
}
- DbTxn* ptxn = bitdb.TxnBegin();
+ DbTxn* ptxn = env->TxnBegin();
for (CDBEnv::KeyValPair& row : salvagedData)
{
if (recoverKVcallback)
@@ -279,8 +313,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return fSuccess;
}
-bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
+bool CDB::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
{
+ std::string walletFile;
+ CDBEnv* env = GetWalletEnv(file_path, walletFile);
+ fs::path walletDir = env->Directory();
+
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
LogPrintf("Using wallet %s\n", walletFile);
@@ -291,7 +329,7 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
return false;
}
- if (!bitdb.Open(walletDir, true)) {
+ if (!env->Open(true /* retry */)) {
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
return false;
}
@@ -299,12 +337,16 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
return true;
}
-bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
+bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
{
+ std::string walletFile;
+ CDBEnv* env = GetWalletEnv(file_path, walletFile);
+ fs::path walletDir = env->Directory();
+
if (fs::exists(walletDir / walletFile))
{
std::string backup_filename;
- CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename);
+ CDBEnv::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
if (r == CDBEnv::RECOVER_OK)
{
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
@@ -414,8 +456,8 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
nFlags |= DB_CREATE;
{
- LOCK(env->cs_db);
- if (!env->Open(GetWalletDir()))
+ LOCK(cs_db);
+ if (!env->Open(false /* retry */))
throw std::runtime_error("CDB: Failed to open database environment.");
pdb = env->mapDb[strFilename];
@@ -442,7 +484,25 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
if (ret != 0) {
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
- CheckUniqueFileid(*env, strFilename, *pdb_temp);
+
+ // Call CheckUniqueFileid on the containing BDB environment to
+ // avoid BDB data consistency bugs that happen when different data
+ // files in the same environment have the same fileid.
+ //
+ // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
+ // bitcoin from opening the same data file through another
+ // environment when the file is referenced through equivalent but
+ // not obviously identical symlinked or hard linked or bind mounted
+ // paths. In the future a more relaxed check for equal inode and
+ // device ids could be done instead, which would allow opening
+ // different backup copies of a wallet at the same time. Maybe even
+ // more ideally, an exclusive lock for accessing the database could
+ // be implemented, so no equality checks are needed at all. (Newer
+ // versions of BDB have an set_lk_exclusive method for this
+ // purpose, but the older version we use does not.)
+ for (auto& env : g_dbenvs) {
+ CheckUniqueFileid(env.second, strFilename, *pdb_temp);
+ }
pdb = pdb_temp.release();
env->mapDb[strFilename] = pdb;
@@ -490,7 +550,7 @@ void CDB::Close()
Flush();
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
--env->mapFileUseCount[strFile];
}
}
@@ -518,7 +578,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
const std::string& strFile = dbw.strFile;
while (true) {
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
// Flush log data to the dat file
env->CloseDb(strFile);
@@ -646,7 +706,7 @@ bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
bool ret = false;
CDBEnv *env = dbw.env;
const std::string& strFile = dbw.strFile;
- TRY_LOCK(bitdb.cs_db,lockDb);
+ TRY_LOCK(cs_db, lockDb);
if (lockDb)
{
// Don't do this if any databases are in use
@@ -694,7 +754,7 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
while (true)
{
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
{
// Flush log data to the dat file
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 787135e400..b1ce451534 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -11,6 +11,7 @@
#include <serialize.h>
#include <streams.h>
#include <sync.h>
+#include <util.h>
#include <version.h>
#include <atomic>
@@ -32,20 +33,19 @@ private:
// shutdown problems/crashes caused by a static initialized internal pointer.
std::string strPath;
- void EnvShutdown();
-
public:
- mutable CCriticalSection cs_db;
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
- CDBEnv();
+ CDBEnv(const fs::path& env_directory);
~CDBEnv();
void Reset();
void MakeMock();
bool IsMock() const { return fMockDb; }
+ bool IsInitialized() const { return fDbEnvInit; }
+ fs::path Directory() const { return strPath; }
/**
* Verify that database file strFile is OK. If it is not,
@@ -56,7 +56,7 @@ public:
enum VerifyResult { VERIFY_OK,
RECOVER_OK,
RECOVER_FAIL };
- typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename);
+ typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
/**
* Salvage data from a file that Verify says is bad.
@@ -68,7 +68,7 @@ public:
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
- bool Open(const fs::path& path, bool retry = 0);
+ bool Open(bool retry);
void Close();
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
@@ -85,7 +85,8 @@ public:
}
};
-extern CDBEnv bitdb;
+/** Get CDBEnv and database filename given a wallet path. */
+CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
@@ -100,9 +101,33 @@ public:
}
/** Create DB handle to real database */
- CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in)
+ CWalletDBWrapper(const fs::path& wallet_path, bool mock = false) :
+ nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
{
+ env = GetWalletEnv(wallet_path, strFile);
+ if (mock) {
+ env->Close();
+ env->Reset();
+ env->MakeMock();
+ }
+ }
+
+ /** Return object for accessing database at specified path. */
+ static std::unique_ptr<CWalletDBWrapper> Create(const fs::path& path)
+ {
+ return MakeUnique<CWalletDBWrapper>(path);
+ }
+
+ /** Return object for accessing dummy database with no read/write capabilities. */
+ static std::unique_ptr<CWalletDBWrapper> CreateDummy()
+ {
+ return MakeUnique<CWalletDBWrapper>();
+ }
+
+ /** Return object for accessing temporary in-memory database. */
+ static std::unique_ptr<CWalletDBWrapper> CreateMock()
+ {
+ return MakeUnique<CWalletDBWrapper>("", true /* mock */);
}
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
@@ -113,10 +138,6 @@ public:
*/
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);
@@ -161,15 +182,15 @@ public:
void Flush();
void Close();
- static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
+ static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
/* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */
static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
+ static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
+ static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
public:
template <typename K, typename T>
@@ -329,7 +350,7 @@ public:
{
if (!pdb || activeTxn)
return false;
- DbTxn* ptxn = bitdb.TxnBegin();
+ DbTxn* ptxn = env->TxnBegin();
if (!ptxn)
return false;
activeTxn = ptxn;
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 73985dcf25..385fdc963a 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -53,6 +53,9 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c
// if we don't have enough data for estimateSmartFee, then use fallbackFee
fee_needed = CWallet::fallbackFee.GetFee(nTxBytes);
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
+
+ // directly return if fallback fee is disabled (feerate 0 == disabled)
+ if (CWallet::fallbackFee.GetFee(1000) == 0) return fee_needed;
}
// Obey mempool min fee when using smart fee estimation
CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 74036f4f0f..e028cf4210 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,6 +5,7 @@
#include <wallet/init.h>
+#include <chainparams.h>
#include <net.h>
#include <util.h>
#include <utilmoneystr.h>
@@ -34,7 +35,7 @@ std::string GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
- strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT));
+ strUsage += HelpMessageOpt("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)"));
strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"));
strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
@@ -65,7 +66,7 @@ bool WalletParameterInteraction()
return true;
}
- gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
+ gArgs.SoftSetArg("-wallet", "");
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
@@ -123,6 +124,8 @@ bool WalletParameterInteraction()
_("This is the minimum transaction fee you pay on every transaction."));
CWallet::minTxFee = CFeeRate(n);
}
+
+ g_wallet_allow_fallback_fee = Params().IsFallbackFeeEnabled();
if (gArgs.IsArgSet("-fallbackfee"))
{
CAmount nFeePerK = 0;
@@ -132,6 +135,7 @@ bool WalletParameterInteraction()
InitWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
+ g_wallet_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
}
if (gArgs.IsArgSet("-discardfee"))
{
@@ -226,18 +230,22 @@ bool VerifyWallets()
std::set<fs::path> wallet_paths;
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
- if (boost::filesystem::path(walletFile).filename() != walletFile) {
- return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile));
- }
-
- if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
- return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile));
- }
-
+ // Do some checking on wallet path. It should be either a:
+ //
+ // 1. Path where a directory can be created.
+ // 2. Path to an existing directory.
+ // 3. Path to a symlink to a directory.
+ // 4. For backwards compatibility, the name of a data file in -walletdir.
fs::path wallet_path = fs::absolute(walletFile, GetWalletDir());
-
- if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) {
- return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile));
+ fs::file_type path_type = fs::symlink_status(wallet_path).type();
+ if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
+ (path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
+ (path_type == fs::regular_file && fs::path(walletFile).filename() == walletFile))) {
+ return InitError(strprintf(
+ _("Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
+ "database/log.?????????? files can be stored, a location where such a directory could be created, "
+ "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)"),
+ walletFile, GetWalletDir()));
}
if (!wallet_paths.insert(wallet_path).second) {
@@ -245,21 +253,21 @@ bool VerifyWallets()
}
std::string strError;
- if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) {
+ if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) {
return InitError(strError);
}
if (gArgs.GetBoolArg("-salvagewallet", false)) {
// Recover readable keypairs:
- CWallet dummyWallet;
+ CWallet dummyWallet("dummy", CWalletDBWrapper::CreateDummy());
std::string backup_filename;
- if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) {
+ if (!CWalletDB::Recover(wallet_path, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) {
return false;
}
}
std::string strWarning;
- bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError);
+ bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError);
if (!strWarning.empty()) {
InitWarning(strWarning);
}
@@ -280,7 +288,7 @@ bool OpenWallets()
}
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
- CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile);
+ CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
if (!pwallet) {
return false;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 741ea25340..c7c54ecb9b 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
#include <chain.h>
+#include <key_io.h>
#include <rpc/safemode.h>
#include <rpc/server.h>
#include <wallet/init.h>
@@ -28,10 +28,6 @@
#include <univalue.h>
-std::string static EncodeDumpTime(int64_t nTime) {
- return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
-}
-
int64_t static DecodeDumpTime(const std::string &str) {
static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
static const std::locale loc(std::locale::classic(),
@@ -147,13 +143,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strSecret);
-
- if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
-
- CKey key = vchSecret.GetKey();
- if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ CKey key = DecodeSecret(strSecret);
+ if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
@@ -279,7 +270,7 @@ UniValue importaddress(const JSONRPCRequest& request)
);
- std::string strLabel = "";
+ std::string strLabel;
if (!request.params[1].isNull())
strLabel = request.params[1].get_str();
@@ -452,7 +443,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
);
- std::string strLabel = "";
+ std::string strLabel;
if (!request.params[1].isNull())
strLabel = request.params[1].get_str();
@@ -554,9 +545,8 @@ UniValue importwallet(const JSONRPCRequest& request)
boost::split(vstr, line, boost::is_any_of(" "));
if (vstr.size() < 2)
continue;
- CBitcoinSecret vchSecret;
- if (vchSecret.SetString(vstr[0])) {
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(vstr[0]);
+ if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
@@ -659,7 +649,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
if (!pwallet->GetKey(keyid, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
- return CBitcoinSecret(vchSecret).ToString();
+ return EncodeSecret(vchSecret);
}
@@ -728,9 +718,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// produce output
file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
- file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
+ file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
- file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
+ file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()));
file << "\n";
// add the base58check encoded extended master if the wallet uses HD
@@ -742,20 +732,17 @@ UniValue dumpwallet(const JSONRPCRequest& request)
CExtKey masterKey;
masterKey.SetMaster(key.begin(), key.size());
- CBitcoinExtKey b58extkey;
- b58extkey.SetKey(masterKey);
-
- file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n";
+ file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
}
}
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
- std::string strTime = EncodeDumpTime(it->first);
+ std::string strTime = FormatISO8601DateTime(it->first);
std::string strAddr;
std::string strLabel;
CKey key;
if (pwallet->GetKey(keyid, key)) {
- file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
+ file << strprintf("%s %s ", EncodeSecret(key), strTime);
if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) {
file << strprintf("label=%s", strLabel);
} else if (keyid == masterKeyID) {
@@ -778,7 +765,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// get birth times for scripts with metadata
auto it = pwallet->m_script_metadata.find(scriptid);
if (it != pwallet->m_script_metadata.end()) {
- create_time = EncodeDumpTime(it->second.nCreateTime);
+ create_time = FormatISO8601DateTime(it->second.nCreateTime);
}
if(pwallet->GetCScript(scriptid, script)) {
file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time);
@@ -911,17 +898,10 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
for (size_t i = 0; i < keys.size(); i++) {
const std::string& privkey = keys[i].get_str();
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(privkey);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(privkey);
if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
}
CPubKey pubkey = key.GetPubKey();
@@ -1018,16 +998,10 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
const std::string& strPrivkey = keys[0].get_str();
// Checks.
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strPrivkey);
+ CKey key = DecodeSecret(strPrivkey);
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
}
CPubKey pubKey = key.GetPubKey();
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 8b95c56a5f..457abec1bc 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -4,12 +4,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <amount.h>
-#include <base58.h>
#include <chain.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <httpserver.h>
#include <validation.h>
+#include <key_io.h>
#include <net.h>
#include <policy/feerate.h>
#include <policy/fees.h>
@@ -1403,6 +1403,16 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
+ bool has_filtered_address = false;
+ CTxDestination filtered_address = CNoDestination();
+ if (!fByAccounts && params.size() > 3) {
+ if (!IsValidDestinationString(params[3].get_str())) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
+ }
+ filtered_address = DecodeDestination(params[3].get_str());
+ has_filtered_address = true;
+ }
+
// Tally
std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
@@ -1421,6 +1431,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
if (!ExtractDestination(txout.scriptPubKey, address))
continue;
+ if (has_filtered_address && !(filtered_address == address)) {
+ continue;
+ }
+
isminefilter mine = IsMine(*pwallet, address);
if(!(mine & filter))
continue;
@@ -1437,10 +1451,24 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
// Reply
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally;
- for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
- const CTxDestination& dest = item.first;
- const std::string& strAccount = item.second.name;
- std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest);
+
+ // Create mapAddressBook iterator
+ // If we aren't filtering, go from begin() to end()
+ auto start = pwallet->mapAddressBook.begin();
+ auto end = pwallet->mapAddressBook.end();
+ // If we are filtering, find() the applicable entry
+ if (has_filtered_address) {
+ start = pwallet->mapAddressBook.find(filtered_address);
+ if (start != end) {
+ end = std::next(start);
+ }
+ }
+
+ for (auto item_it = start; item_it != end; ++item_it)
+ {
+ const CTxDestination& address = item_it->first;
+ const std::string& strAccount = item_it->second.name;
+ auto it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
@@ -1466,7 +1494,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
UniValue obj(UniValue::VOBJ);
if(fIsWatchonly)
obj.pushKV("involvesWatchonly", true);
- obj.pushKV("address", EncodeDestination(dest));
+ obj.pushKV("address", EncodeDestination(address));
obj.pushKV("account", strAccount);
obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
@@ -1511,15 +1539,15 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 3)
+ if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listreceivedbyaddress ( minconf include_empty include_watchonly)\n"
+ "listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
"3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
-
+ "4. address_filter (string, optional) If present, only return information on this address.\n"
"\nResult:\n"
"[\n"
" {\n"
@@ -1541,6 +1569,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleCli("listreceivedbyaddress", "")
+ HelpExampleCli("listreceivedbyaddress", "6 true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
+ + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"")
);
ObserveSafeMode();
@@ -3837,7 +3866,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} },
{ "wallet", "listlockunspent", &listlockunspent, {} },
{ "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
{ "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index cafd69d075..7b20bd7b02 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -13,13 +13,13 @@
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
static void
-GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results)
+GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results)
{
std::list<CAccountingEntry> aes;
results.clear();
- BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK);
- wallet->ListAccountCreditDebit("", aes);
+ BOOST_CHECK(wallet.ReorderTransactions() == DB_LOAD_OK);
+ wallet.ListAccountCreditDebit("", aes);
for (CAccountingEntry& ae : aes)
{
results[ae.nOrderPos] = ae;
@@ -33,28 +33,28 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
CAccountingEntry ae;
std::map<CAmount, CAccountingEntry> results;
- LOCK(pwalletMain->cs_wallet);
+ LOCK(m_wallet.cs_wallet);
ae.strAccount = "";
ae.nCreditDebit = 1;
ae.nTime = 1333333333;
ae.strOtherAccount = "b";
ae.strComment = "";
- pwalletMain->AddAccountingEntry(ae);
+ m_wallet.AddAccountingEntry(ae);
wtx.mapValue["comment"] = "z";
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[0]->nTimeReceived = (unsigned int)1333333335;
vpwtx[0]->nOrderPos = -1;
ae.nTime = 1333333336;
ae.strOtherAccount = "c";
- pwalletMain->AddAccountingEntry(ae);
+ m_wallet.AddAccountingEntry(ae);
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 3);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 3);
BOOST_CHECK(2 == results.size());
BOOST_CHECK(results[0].nTime == 1333333333);
BOOST_CHECK(results[0].strComment.empty());
@@ -65,13 +65,13 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333330;
ae.strOtherAccount = "d";
- ae.nOrderPos = pwalletMain->IncOrderPosNext();
- pwalletMain->AddAccountingEntry(ae);
+ ae.nOrderPos = m_wallet.IncOrderPosNext();
+ m_wallet.AddAccountingEntry(ae);
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
BOOST_CHECK(results.size() == 3);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 4);
BOOST_CHECK(results[0].nTime == 1333333333);
BOOST_CHECK(1 == vpwtx[0]->nOrderPos);
BOOST_CHECK(results[2].nTime == 1333333336);
@@ -82,28 +82,28 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
wtx.mapValue["comment"] = "y";
{
CMutableTransaction tx(*wtx.tx);
- --tx.nLockTime; // Just to change the hash :)
+ ++tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[1]->nTimeReceived = (unsigned int)1333333336;
wtx.mapValue["comment"] = "x";
{
CMutableTransaction tx(*wtx.tx);
- --tx.nLockTime; // Just to change the hash :)
+ ++tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
vpwtx[2]->nOrderPos = -1;
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
BOOST_CHECK(results.size() == 3);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 6);
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
BOOST_CHECK(results[1].nTime == 1333333333);
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
@@ -116,12 +116,12 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333334;
ae.strOtherAccount = "e";
ae.nOrderPos = -1;
- pwalletMain->AddAccountingEntry(ae);
+ m_wallet.AddAccountingEntry(ae);
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
BOOST_CHECK(results.size() == 4);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 7);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 7);
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
BOOST_CHECK(results[1].nTime == 1333333333);
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 7797f85f07..77ccd0b8d8 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,27 +6,21 @@
#include <rpc/server.h>
#include <wallet/db.h>
+#include <wallet/wallet.h>
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
- TestingSetup(chainName)
+ TestingSetup(chainName), m_wallet("mock", CWalletDBWrapper::CreateMock())
{
- bitdb.MakeMock();
-
bool fFirstRun;
g_address_type = OUTPUT_TYPE_DEFAULT;
g_change_type = OUTPUT_TYPE_DEFAULT;
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
- pwalletMain = MakeUnique<CWallet>(std::move(dbw));
- pwalletMain->LoadWallet(fFirstRun);
- RegisterValidationInterface(pwalletMain.get());
+ m_wallet.LoadWallet(fFirstRun);
+ RegisterValidationInterface(&m_wallet);
RegisterWalletRPCCommands(tableRPC);
}
WalletTestingSetup::~WalletTestingSetup()
{
- UnregisterValidationInterface(pwalletMain.get());
-
- bitdb.Flush(true);
- bitdb.Reset();
+ UnregisterValidationInterface(&m_wallet);
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index c03aec7f87..663836a955 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -15,7 +15,7 @@ struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~WalletTestingSetup();
- std::unique_ptr<CWallet> pwalletMain;
+ CWallet m_wallet;
};
#endif
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 9db5d63922..41348b50a4 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -36,7 +36,7 @@ typedef std::set<CInputCoin> CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
-static const CWallet testWallet;
+static const CWallet testWallet("dummy", CWalletDBWrapper::CreateDummy());
static std::vector<COutput> vCoins;
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
@@ -382,7 +382,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -397,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -409,7 +409,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
vpwallets.insert(vpwallets.begin(), &wallet);
UniValue keys;
keys.setArray();
@@ -471,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
LOCK(wallet.cs_wallet);
wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
@@ -486,7 +486,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
@@ -516,7 +516,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
LOCK2(cs_main, wallet.cs_wallet);
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
@@ -562,27 +562,25 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
// expanded to cover more corner cases of smart time logic.
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
{
- CWallet wallet;
-
// New transaction should use clock time if lower than block time.
- BOOST_CHECK_EQUAL(AddTx(wallet, 1, 100, 120), 100);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
// Test that updating existing transaction does not change smart time.
- BOOST_CHECK_EQUAL(AddTx(wallet, 1, 200, 220), 100);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
// New transaction should use clock time if there's no block time.
- BOOST_CHECK_EQUAL(AddTx(wallet, 2, 300, 0), 300);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
// New transaction should use block time if lower than clock time.
- BOOST_CHECK_EQUAL(AddTx(wallet, 3, 420, 400), 400);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
// New transaction should use latest entry time if higher than
// min(block time, clock time).
- BOOST_CHECK_EQUAL(AddTx(wallet, 4, 500, 390), 400);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
// If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time.
- BOOST_CHECK_EQUAL(AddTx(wallet, 5, 50, 600), 300);
+ BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
// Reset mock time for other tests.
SetMockTime(0);
@@ -591,12 +589,12 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
CTxDestination dest = CKeyID();
- LOCK(pwalletMain->cs_wallet);
- pwalletMain->AddDestData(dest, "misc", "val_misc");
- pwalletMain->AddDestData(dest, "rr0", "val_rr0");
- pwalletMain->AddDestData(dest, "rr1", "val_rr1");
+ LOCK(m_wallet.cs_wallet);
+ m_wallet.AddDestData(dest, "misc", "val_misc");
+ m_wallet.AddDestData(dest, "rr0", "val_rr0");
+ m_wallet.AddDestData(dest, "rr1", "val_rr1");
- auto values = pwalletMain->GetDestValues("rr");
+ auto values = m_wallet.GetDestValues("rr");
BOOST_CHECK_EQUAL(values.size(), 2);
BOOST_CHECK_EQUAL(values[0], "val_rr0");
BOOST_CHECK_EQUAL(values[1], "val_rr1");
@@ -608,10 +606,9 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- ::bitdb.MakeMock();
g_address_type = OUTPUT_TYPE_DEFAULT;
g_change_type = OUTPUT_TYPE_DEFAULT;
- wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat"))));
+ wallet = MakeUnique<CWallet>("mock", CWalletDBWrapper::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
@@ -623,8 +620,6 @@ public:
~ListCoinsTestingSetup()
{
wallet.reset();
- ::bitdb.Flush(true);
- ::bitdb.Reset();
}
CWalletTx& AddTx(CRecipient recipient)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b35f8c7f2b..0c468878e1 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -5,7 +5,6 @@
#include <wallet/wallet.h>
-#include <base58.h>
#include <checkpoints.h>
#include <chain.h>
#include <wallet/coincontrol.h>
@@ -14,6 +13,7 @@
#include <fs.h>
#include <wallet/init.h>
#include <key.h>
+#include <key_io.h>
#include <keystore.h>
#include <validation.h>
#include <net.h>
@@ -43,8 +43,8 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fWalletRbf = DEFAULT_WALLET_RBF;
OutputType g_address_type = OUTPUT_TYPE_NONE;
OutputType g_change_type = OUTPUT_TYPE_NONE;
+bool g_wallet_allow_fallback_fee = true; //<! will be defined via chainparams
-const char * DEFAULT_WALLET_DAT = "wallet.dat";
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
/**
@@ -2918,6 +2918,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
}
nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
+ return false;
+ }
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
@@ -3902,16 +3907,17 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
+CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
{
+ const std::string& walletFile = name;
+
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3923,8 +3929,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
- CWallet *walletInstance = new CWallet(std::move(dbw));
+ CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index fefe415bb1..3e2d1794d8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -15,6 +15,7 @@
#include <validationinterface.h>
#include <script/ismine.h>
#include <script/sign.h>
+#include <util.h>
#include <wallet/crypter.h>
#include <wallet/walletdb.h>
#include <wallet/rpcwallet.h>
@@ -39,6 +40,7 @@ extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fWalletRbf;
+extern bool g_wallet_allow_fallback_fee;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
@@ -66,8 +68,6 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
-extern const char * DEFAULT_WALLET_DAT;
-
static const int64_t TIMESTAMP_MIN = 0;
class CBlockIndex;
@@ -736,6 +736,14 @@ private:
*/
bool AddWatchOnly(const CScript& dest) override;
+ /**
+ * Wallet filename from wallet=<path> command line or config option.
+ * Used in debug logs and to send RPCs to the right wallet instance when
+ * more than one wallet is loaded.
+ */
+ std::string m_name;
+
+ /** Internal database handle. */
std::unique_ptr<CWalletDBWrapper> dbw;
/**
@@ -767,14 +775,7 @@ public:
/** Get a name for this wallet for logging/debugging purposes.
*/
- std::string GetName() const
- {
- if (dbw) {
- return dbw->GetName();
- } else {
- return "dummy";
- }
- }
+ const std::string& GetName() const { return m_name; }
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
@@ -788,14 +789,8 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
- // Create wallet with dummy database handle
- CWallet(): dbw(new CWalletDBWrapper())
- {
- SetNull();
- }
-
- // Create wallet with passed-in database handle
- explicit CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
+ /** Construct wallet with specified name and database implementation. */
+ CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw))
{
SetNull();
}
@@ -1115,7 +1110,7 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static CWallet* CreateWalletFromFile(const std::string walletFile);
+ static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path);
/**
* Wallet post-init setup
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index dd6835a06f..0b0880a2ba 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -5,10 +5,10 @@
#include <wallet/walletdb.h>
-#include <base58.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <key_io.h>
#include <protocol.h>
#include <serialize.h>
#include <sync.h>
@@ -771,16 +771,16 @@ void MaybeCompactWalletDB()
//
// Try to (very carefully!) recover wallet file if there is a problem.
//
-bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
+bool CWalletDB::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
{
- return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename);
+ return CDB::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
}
-bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename)
+bool CWalletDB::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
{
// recover without a key filter callback
// results in recovering all record types
- return CWalletDB::Recover(filename, nullptr, nullptr, out_backup_filename);
+ return CWalletDB::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
}
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
@@ -806,14 +806,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
return true;
}
-bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
+bool CWalletDB::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
{
- return CDB::VerifyEnvironment(walletFile, walletDir, errorStr);
+ return CDB::VerifyEnvironment(wallet_path, errorStr);
}
-bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr)
+bool CWalletDB::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr)
{
- return CDB::VerifyDatabaseFile(walletFile, walletDir, warningStr, errorStr, CWalletDB::Recover);
+ return CDB::VerifyDatabaseFile(wallet_path, warningStr, errorStr, CWalletDB::Recover);
}
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 3691cfcb57..7d754c7284 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -218,17 +218,17 @@ public:
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
/* Try to (very carefully!) recover wallet database (with a possible key type filter) */
- static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
+ static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
/* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */
- static bool Recover(const std::string& filename, std::string& out_backup_filename);
+ static bool Recover(const fs::path& wallet_path, std::string& out_backup_filename);
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
+ static bool VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr);
+ static bool VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);