aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-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/bitcoin-tx.cpp10
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/key_io.cpp223
-rw-r--r--src/key_io.h29
-rw-r--r--src/net.cpp9
-rw-r--r--src/net_processing.cpp30
-rw-r--r--src/net_processing.h18
-rw-r--r--src/qt/addresstablemodel.cpp3
-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/guiutil.cpp3
-rw-r--r--src/qt/paymentrequestplus.cpp1
-rw-r--r--src/qt/paymentrequestplus.h3
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/sendcoinsdialog.cpp8
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/paymentservertests.cpp1
-rw-r--r--src/qt/test/wallettests.cpp1
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h5
-rw-r--r--src/rpc/blockchain.cpp12
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/misc.cpp11
-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/test/addrman_tests.cpp187
-rw-r--r--src/test/base58_tests.cpp140
-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/rpc_tests.cpp2
-rw-r--r--src/validation.cpp55
-rw-r--r--src/wallet/rpcdump.cpp46
-rw-r--r--src/wallet/rpcwallet.cpp2
-rw-r--r--src/wallet/wallet.cpp2
-rw-r--r--src/wallet/walletdb.cpp2
52 files changed, 926 insertions, 598 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8de7f7ff7a..0a9370c85c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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.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/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/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/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/key_io.cpp b/src/key_io.cpp
new file mode 100644
index 0000000000..20df945d8d
--- /dev/null
+++ b/src/key_io.cpp
@@ -0,0 +1,223 @@
+// 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};
+ 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
+
+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/net.cpp b/src/net.cpp
index c7c19f83a1..f7e6c300b1 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1828,11 +1828,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))
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index ddf0dbbbea..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,6 +906,10 @@ 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);
@@ -920,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);
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/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/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/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..8ad4fa31f1 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>
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ef36aab1a4..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>
@@ -376,6 +376,12 @@ void SendCoinsDialog::on_sendButton_clicked()
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/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..5ba75cc91d 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>
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 6f30e56327..c3f7ea601e 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -9,8 +9,8 @@
#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>
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index de3e885e8f..2830149655 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>
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/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 8007cebc37..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)
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/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/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/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/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/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/validation.cpp b/src/validation.cpp
index a77362f5d6..51e40c17b5 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1856,12 +1856,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))) {
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 930e8bbbb4..0edc8d8d66 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>
@@ -147,13 +147,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));
@@ -554,9 +549,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 +653,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);
}
@@ -742,10 +736,7 @@ 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++) {
@@ -755,7 +746,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
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) {
@@ -911,17 +902,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 +1002,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);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
+ CKey key = DecodeSecret(strPrivkey);
- 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 dfa8546076..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>
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index bb7be2df33..a014f5b2a0 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>
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index dd6835a06f..c14fbd1e8c 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>