aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/init.cpp2
-rw-r--r--src/net.cpp2
-rw-r--r--src/net.h4
-rw-r--r--src/net_processing.cpp2
-rw-r--r--src/netaddress.cpp97
-rw-r--r--src/netaddress.h6
-rw-r--r--src/qt/peertablemodel.cpp4
-rw-r--r--src/qt/peertablemodel.h4
-rw-r--r--src/test/fuzz/asmap.cpp28
-rw-r--r--src/txmempool.cpp49
-rw-r--r--src/txmempool.h52
-rw-r--r--src/util/asmap.cpp42
-rw-r--r--src/validation.cpp6
-rw-r--r--src/wallet/rpcwallet.cpp23
15 files changed, 231 insertions, 97 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 9d782e7a04..c76f30de8e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -7,6 +7,7 @@ FUZZ_TARGETS = \
test/fuzz/addr_info_deserialize \
test/fuzz/address_deserialize \
test/fuzz/addrman_deserialize \
+ test/fuzz/asmap \
test/fuzz/banentry_deserialize \
test/fuzz/base_encode_decode \
test/fuzz/bech32 \
@@ -255,6 +256,12 @@ test_fuzz_addrman_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_addrman_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_addrman_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_asmap_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_asmap_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_asmap_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_asmap_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_asmap_SOURCES = $(FUZZ_SUITE) test/fuzz/asmap.cpp
+
test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1
test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/init.cpp b/src/init.cpp
index e1a02edb96..90d2624c7f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1835,8 +1835,8 @@ bool AppInitMain(NodeContext& node)
InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path));
return false;
}
- node.connman->SetAsmap(asmap);
const uint256 asmap_version = SerializeHash(asmap);
+ node.connman->SetAsmap(std::move(asmap));
LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString());
} else {
LogPrintf("Using /16 prefix for IP bucketing.\n");
diff --git a/src/net.cpp b/src/net.cpp
index 9cd2d30d9d..18fe95e675 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -498,7 +498,7 @@ void CNode::SetAddrLocal(const CService& addrLocalIn) {
#undef X
#define X(name) stats.name = name
-void CNode::copyStats(CNodeStats &stats, std::vector<bool> &m_asmap)
+void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
{
stats.nodeid = this->GetId();
X(nServices);
diff --git a/src/net.h b/src/net.h
index f23cae323e..819947658a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -331,7 +331,7 @@ public:
*/
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
- void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = asmap; }
+ void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
private:
struct ListenSocket {
@@ -983,7 +983,7 @@ public:
void CloseSocketDisconnect();
- void copyStats(CNodeStats &stats, std::vector<bool> &m_asmap);
+ void copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap);
ServiceFlags GetLocalServices() const
{
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 81deaea0df..cf4aee0647 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1182,7 +1182,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
{
LOCK(g_cs_recent_confirmed_transactions);
- for (const auto ptx : pblock->vtx) {
+ for (const auto& ptx : pblock->vtx) {
g_recent_confirmed_transactions->insert(ptx->GetHash());
}
}
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index ce3e17197e..1cac57a817 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -401,6 +401,26 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
return true;
}
+bool CNetAddr::HasLinkedIPv4() const
+{
+ return IsRoutable() && (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380());
+}
+
+uint32_t CNetAddr::GetLinkedIPv4() const
+{
+ if (IsIPv4() || IsRFC6145() || IsRFC6052()) {
+ // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
+ return ReadBE32(ip + 12);
+ } else if (IsRFC3964()) {
+ // 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6
+ return ReadBE32(ip + 2);
+ } else if (IsRFC4380()) {
+ // Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped
+ return ~ReadBE32(ip + 12);
+ }
+ assert(false);
+}
+
uint32_t CNetAddr::GetNetClass() const {
uint32_t net_class = NET_IPV6;
if (IsLocal()) {
@@ -410,7 +430,7 @@ uint32_t CNetAddr::GetNetClass() const {
net_class = NET_INTERNAL;
} else if (!IsRoutable()) {
net_class = NET_UNROUTABLE;
- } else if (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380()) {
+ } else if (HasLinkedIPv4()) {
net_class = NET_IPV4;
} else if (IsTor()) {
net_class = NET_ONION;
@@ -424,10 +444,24 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
}
std::vector<bool> ip_bits(128);
- for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
- uint8_t cur_byte = GetByte(15 - byte_i);
- for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
- ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
+ if (HasLinkedIPv4()) {
+ // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits)
+ for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
+ for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
+ ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1;
+ }
+ }
+ uint32_t ipv4 = GetLinkedIPv4();
+ for (int i = 0; i < 32; ++i) {
+ ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
+ }
+ } else {
+ // Use all 128 bits of the IPv6 address otherwise
+ for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
+ uint8_t cur_byte = GetByte(15 - byte_i);
+ for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
+ ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
+ }
}
}
uint32_t mapped_as = Interpret(asmap, ip_bits);
@@ -463,51 +497,32 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
int nStartByte = 0;
int nBits = 16;
- // all local addresses belong to the same group
- if (IsLocal())
- {
+ if (IsLocal()) {
+ // all local addresses belong to the same group
nBits = 0;
- }
- // all internal-usage addresses get their own group
- if (IsInternal())
- {
+ } else if (IsInternal()) {
+ // all internal-usage addresses get their own group
nStartByte = sizeof(g_internal_prefix);
nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8;
- }
- // all other unroutable addresses belong to the same group
- else if (!IsRoutable())
- {
+ } else if (!IsRoutable()) {
+ // all other unroutable addresses belong to the same group
nBits = 0;
- }
- // for IPv4 addresses, '1' + the 16 higher-order bits of the IP
- // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix
- else if (IsIPv4() || IsRFC6145() || IsRFC6052())
- {
- nStartByte = 12;
- }
- // for 6to4 tunnelled addresses, use the encapsulated IPv4 address
- else if (IsRFC3964())
- {
- nStartByte = 2;
- }
- // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address
- else if (IsRFC4380())
- {
- vchRet.push_back(GetByte(3) ^ 0xFF);
- vchRet.push_back(GetByte(2) ^ 0xFF);
+ } else if (HasLinkedIPv4()) {
+ // IPv4 addresses (and mapped IPv4 addresses) use /16 groups
+ uint32_t ipv4 = GetLinkedIPv4();
+ vchRet.push_back((ipv4 >> 24) & 0xFF);
+ vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
- }
- else if (IsTor())
- {
+ } else if (IsTor()) {
nStartByte = 6;
nBits = 4;
- }
- // for he.net, use /36 groups
- else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
+ } else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) {
+ // for he.net, use /36 groups
nBits = 36;
- // for the rest of the IPv6 network, use /32 groups
- else
+ } else {
+ // for the rest of the IPv6 network, use /32 groups
nBits = 32;
+ }
// push our ip onto vchRet byte by byte...
while (nBits >= 8)
diff --git a/src/netaddress.h b/src/netaddress.h
index 078234595c..b300b709f3 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -39,7 +39,6 @@ class CNetAddr
explicit CNetAddr(const struct in_addr& ipv4Addr);
void SetIP(const CNetAddr& ip);
- private:
/**
* Set raw IPv4 or IPv6 address (in network byte order)
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
@@ -80,6 +79,11 @@ class CNetAddr
bool GetInAddr(struct in_addr* pipv4Addr) const;
uint32_t GetNetClass() const;
+ //! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
+ uint32_t GetLinkedIPv4() const;
+ //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
+ bool HasLinkedIPv4() const;
+
// The AS on the BGP path to the node we use to diversify
// peers in AddrMan bucketing based on the AS infrastructure.
// The ip->AS mapping depends on how asmap is constructed.
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 631c66e745..a497f58b16 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -4,7 +4,6 @@
#include <qt/peertablemodel.h>
-#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
@@ -100,10 +99,9 @@ public:
}
};
-PeerTableModel::PeerTableModel(interfaces::Node& node, ClientModel *parent) :
+PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) :
QAbstractTableModel(parent),
m_node(node),
- clientModel(parent),
timer(nullptr)
{
columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent");
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index b3f5dd7dbe..cf45c5a08f 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -13,7 +13,6 @@
#include <QAbstractTableModel>
#include <QStringList>
-class ClientModel;
class PeerTablePriv;
namespace interfaces {
@@ -51,7 +50,7 @@ class PeerTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit PeerTableModel(interfaces::Node& node, ClientModel *parent = nullptr);
+ explicit PeerTableModel(interfaces::Node& node, QObject* parent);
~PeerTableModel();
const CNodeCombinedStats *getNodeStats(int idx);
int getRowByNodeId(NodeId nodeid);
@@ -83,7 +82,6 @@ public Q_SLOTS:
private:
interfaces::Node& m_node;
- ClientModel *clientModel;
QStringList columns;
std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
new file mode 100644
index 0000000000..7f3eef79a1
--- /dev/null
+++ b/src/test/fuzz/asmap.cpp
@@ -0,0 +1,28 @@
+// Copyright (c) 2020 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 <netaddress.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const Network network = fuzzed_data_provider.PickValueInArray({NET_IPV4, NET_IPV6});
+ if (fuzzed_data_provider.remaining_bytes() < 16) {
+ return;
+ }
+ CNetAddr net_addr;
+ net_addr.SetRaw(network, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data());
+ std::vector<bool> asmap;
+ for (const char cur_byte : fuzzed_data_provider.ConsumeRemainingBytes<char>()) {
+ for (int bit = 0; bit < 8; ++bit) {
+ asmap.push_back((cur_byte >> bit) & 1);
+ }
+ }
+ (void)net_addr.GetMappedAS(asmap);
+}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 441255182e..5768219f3a 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -23,7 +23,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe
int64_t _nTime, unsigned int _entryHeight,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp)
: tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight),
- spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
+ spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), m_epoch(0)
{
nCountWithDescendants = 1;
nSizeWithDescendants = GetTxSize();
@@ -122,8 +122,6 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// setMemPoolChildren will be updated, an assumption made in
// UpdateForDescendants.
for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) {
- // we cache the in-mempool children to avoid duplicate updates
- setEntries setChildren;
// calculate children from mapNextTx
txiter it = mapTx.find(hash);
if (it == mapTx.end()) {
@@ -132,17 +130,21 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
// First calculate the children, and update setMemPoolChildren to
// include them, and update their setMemPoolParents to include this tx.
- for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
- const uint256 &childHash = iter->second->GetHash();
- txiter childIter = mapTx.find(childHash);
- assert(childIter != mapTx.end());
- // We can skip updating entries we've encountered before or that
- // are in the block (which are already accounted for).
- if (setChildren.insert(childIter).second && !setAlreadyIncluded.count(childHash)) {
- UpdateChild(it, childIter, true);
- UpdateParent(childIter, it, true);
+ // we cache the in-mempool children to avoid duplicate updates
+ {
+ const auto epoch = GetFreshEpoch();
+ for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
+ const uint256 &childHash = iter->second->GetHash();
+ txiter childIter = mapTx.find(childHash);
+ assert(childIter != mapTx.end());
+ // We can skip updating entries we've encountered before or that
+ // are in the block (which are already accounted for).
+ if (!visited(childIter) && !setAlreadyIncluded.count(childHash)) {
+ UpdateChild(it, childIter, true);
+ UpdateParent(childIter, it, true);
+ }
}
- }
+ } // release epoch guard for UpdateForDescendants
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded);
}
}
@@ -325,7 +327,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
}
CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator)
- : nTransactionsUpdated(0), minerPolicyEstimator(estimator)
+ : nTransactionsUpdated(0), minerPolicyEstimator(estimator), m_epoch(0), m_has_epoch_guard(false)
{
_clear(); //lock free clear
@@ -1105,4 +1107,23 @@ void CTxMemPool::SetIsLoaded(bool loaded)
m_is_loaded = loaded;
}
+
+CTxMemPool::EpochGuard CTxMemPool::GetFreshEpoch() const
+{
+ return EpochGuard(*this);
+}
+CTxMemPool::EpochGuard::EpochGuard(const CTxMemPool& in) : pool(in)
+{
+ assert(!pool.m_has_epoch_guard);
+ ++pool.m_epoch;
+ pool.m_has_epoch_guard = true;
+}
+
+CTxMemPool::EpochGuard::~EpochGuard()
+{
+ // prevents stale results being used
+ ++pool.m_epoch;
+ pool.m_has_epoch_guard = false;
+}
+
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
diff --git a/src/txmempool.h b/src/txmempool.h
index 01db59e859..de11d626b4 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -129,6 +129,7 @@ public:
int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
+ mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms
};
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
@@ -453,6 +454,8 @@ private:
mutable int64_t lastRollingFeeUpdate;
mutable bool blockSinceLastRollingFeeBump;
mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially
+ mutable uint64_t m_epoch;
+ mutable bool m_has_epoch_guard;
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -736,6 +739,55 @@ private:
* removal.
*/
void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
+public:
+ /** EpochGuard: RAII-style guard for using epoch-based graph traversal algorithms.
+ * When walking ancestors or descendants, we generally want to avoid
+ * visiting the same transactions twice. Some traversal algorithms use
+ * std::set (or setEntries) to deduplicate the transaction we visit.
+ * However, use of std::set is algorithmically undesirable because it both
+ * adds an asymptotic factor of O(log n) to traverals cost and triggers O(n)
+ * more dynamic memory allocations.
+ * In many algorithms we can replace std::set with an internal mempool
+ * counter to track the time (or, "epoch") that we began a traversal, and
+ * check + update a per-transaction epoch for each transaction we look at to
+ * determine if that transaction has not yet been visited during the current
+ * traversal's epoch.
+ * Algorithms using std::set can be replaced on a one by one basis.
+ * Both techniques are not fundamentally incomaptible across the codebase.
+ * Generally speaking, however, the remaining use of std::set for mempool
+ * traversal should be viewed as a TODO for replacement with an epoch based
+ * traversal, rather than a preference for std::set over epochs in that
+ * algorithm.
+ */
+ class EpochGuard {
+ const CTxMemPool& pool;
+ public:
+ EpochGuard(const CTxMemPool& in);
+ ~EpochGuard();
+ };
+ // N.B. GetFreshEpoch modifies mutable state via the EpochGuard construction
+ // (and later destruction)
+ EpochGuard GetFreshEpoch() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** visited marks a CTxMemPoolEntry as having been traversed
+ * during the lifetime of the most recently created EpochGuard
+ * and returns false if we are the first visitor, true otherwise.
+ *
+ * An EpochGuard must be held when visited is called or an assert will be
+ * triggered.
+ *
+ */
+ bool visited(txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ assert(m_has_epoch_guard);
+ bool ret = it->m_epoch >= m_epoch;
+ it->m_epoch = std::max(it->m_epoch, m_epoch);
+ return ret;
+ }
+
+ bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ assert(m_has_epoch_guard);
+ return !it || visited(*it);
+ }
};
/**
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index ac230e9ee5..60bd27bf90 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -8,13 +8,14 @@
namespace {
-uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes)
+uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes)
{
uint32_t val = minval;
bool bit;
for (std::vector<uint8_t>::const_iterator bit_sizes_it = bit_sizes.begin();
bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) {
if (bit_sizes_it + 1 != bit_sizes.end()) {
+ if (bitpos == endpos) break;
bit = *bitpos;
bitpos++;
} else {
@@ -24,6 +25,7 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, c
val += (1 << *bit_sizes_it);
} else {
for (int b = 0; b < *bit_sizes_it; b++) {
+ if (bitpos == endpos) break;
bit = *bitpos;
bitpos++;
val += bit << (*bit_sizes_it - 1 - b);
@@ -35,29 +37,29 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, c
}
const std::vector<uint8_t> TYPE_BIT_SIZES{0, 0, 1};
-uint32_t DecodeType(std::vector<bool>::const_iterator& bitpos)
+uint32_t DecodeType(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
- return DecodeBits(bitpos, 0, TYPE_BIT_SIZES);
+ return DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES);
}
const std::vector<uint8_t> ASN_BIT_SIZES{15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
-uint32_t DecodeASN(std::vector<bool>::const_iterator& bitpos)
+uint32_t DecodeASN(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
- return DecodeBits(bitpos, 1, ASN_BIT_SIZES);
+ return DecodeBits(bitpos, endpos, 1, ASN_BIT_SIZES);
}
const std::vector<uint8_t> MATCH_BIT_SIZES{1, 2, 3, 4, 5, 6, 7, 8};
-uint32_t DecodeMatch(std::vector<bool>::const_iterator& bitpos)
+uint32_t DecodeMatch(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
- return DecodeBits(bitpos, 2, MATCH_BIT_SIZES);
+ return DecodeBits(bitpos, endpos, 2, MATCH_BIT_SIZES);
}
const std::vector<uint8_t> JUMP_BIT_SIZES{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
-uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos)
+uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
- return DecodeBits(bitpos, 17, JUMP_BIT_SIZES);
+ return DecodeBits(bitpos, endpos, 17, JUMP_BIT_SIZES);
}
}
@@ -65,33 +67,37 @@ uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos)
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip)
{
std::vector<bool>::const_iterator pos = asmap.begin();
+ const std::vector<bool>::const_iterator endpos = asmap.end();
uint8_t bits = ip.size();
- uint8_t default_asn = 0;
+ uint32_t default_asn = 0;
uint32_t opcode, jump, match, matchlen;
- while (1) {
- assert(pos != asmap.end());
- opcode = DecodeType(pos);
+ while (pos != endpos) {
+ opcode = DecodeType(pos, endpos);
if (opcode == 0) {
- return DecodeASN(pos);
+ return DecodeASN(pos, endpos);
} else if (opcode == 1) {
- jump = DecodeJump(pos);
+ jump = DecodeJump(pos, endpos);
+ if (bits == 0) break;
if (ip[ip.size() - bits]) {
+ if (jump >= endpos - pos) break;
pos += jump;
}
bits--;
} else if (opcode == 2) {
- match = DecodeMatch(pos);
+ match = DecodeMatch(pos, endpos);
matchlen = CountBits(match) - 1;
for (uint32_t bit = 0; bit < matchlen; bit++) {
+ if (bits == 0) break;
if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) {
return default_asn;
}
bits--;
}
} else if (opcode == 3) {
- default_asn = DecodeASN(pos);
+ default_asn = DecodeASN(pos, endpos);
} else {
- assert(0);
+ break;
}
}
+ return 0; // 0 is not a valid ASN
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 9854740e6f..bab04b8e34 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1392,10 +1392,14 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
CheckForkWarningConditions();
}
+// Called both upon regular invalid block discovery *and* InvalidateBlock
void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
+ if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
+ pindexBestHeader = ::ChainActive().Tip();
+ }
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
@@ -1408,6 +1412,8 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
CheckForkWarningConditions();
}
+// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
+// which does its own setBlockIndexCandidates manageent.
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) {
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index df71f97e85..ab0819a821 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3766,18 +3766,18 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
" hdseedid) and relation to the wallet (ismine, iswatchonly).\n"
" \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n"
- " \"label\" : \"label\" (string) The label associated with the address. Defaults to \"\". Equivalent to the label name in the labels array below.\n"
+ " \"label\" : \"label\" (string) DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below.\n"
" \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + ".\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n"
" \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n"
" \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n"
- " \"labels\" (json object) An array of labels associated with the address. Currently limited to one label but returned\n"
- " as an array to keep the API stable if multiple labels are enabled in the future.\n"
+ " \"labels\" (array) Array of labels associated with the address. Currently limited to one label but returned\n"
+ " as an array to keep the API stable if multiple labels are enabled in the future.\n"
" [\n"
- " \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n\n"
+ " \"label name\" (string) The label name. Defaults to \"\".\n"
" DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`:\n"
- " { (json object of label data)\n"
- " \"name\" : \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n"
+ " {\n"
+ " \"name\" : \"label name\" (string) The label name. Defaults to \"\".\n"
" \"purpose\" : \"purpose\" (string) The purpose of the associated address (send or receive).\n"
" }\n"
" ]\n"
@@ -3821,10 +3821,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
- // Return label field if existing. Currently only one label can be
- // associated with an address, so the label should be equivalent to the
+ // DEPRECATED: Return label field if existing. Currently only one label can
+ // be associated with an address, so the label should be equivalent to the
// value of the name key/value pair in the labels array below.
- if (pwallet->mapAddressBook.count(dest)) {
+ if ((pwallet->chain().rpcEnableDeprecated("label")) && (pwallet->mapAddressBook.count(dest))) {
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
}
@@ -3847,12 +3847,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// associated with an address, but we return an array so the API remains
// stable if we allow multiple labels to be associated with an address in
// the future.
- //
- // DEPRECATED: The previous behavior of returning an array containing a JSON
- // object of `name` and `purpose` key/value pairs has been deprecated.
UniValue labels(UniValue::VARR);
std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end()) {
+ // DEPRECATED: The previous behavior of returning an array containing a
+ // JSON object of `name` and `purpose` key/value pairs is deprecated.
if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
labels.push_back(AddressBookDataToJSON(mi->second, true));
} else {