aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/bench/Examples.cpp2
-rw-r--r--src/bench/base58.cpp2
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bench/checkblock.cpp3
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/bitcoin-tx.cpp6
-rw-r--r--src/blockencodings.cpp2
-rw-r--r--src/checkpoints.cpp2
-rw-r--r--src/core_io.h3
-rw-r--r--src/core_read.cpp2
-rw-r--r--src/init.cpp5
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net_processing.cpp3025
-rw-r--r--src/net_processing.h51
-rw-r--r--src/netbase.cpp48
-rw-r--r--src/policy/fees.cpp7
-rw-r--r--src/policy/policy.cpp2
-rw-r--r--src/primitives/transaction.cpp27
-rw-r--r--src/primitives/transaction.h150
-rw-r--r--src/qt/bitcoingui.cpp32
-rw-r--r--src/qt/bitcoingui.h19
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/qt/coincontroldialog.cpp18
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp9
-rw-r--r--src/qt/guiutil.h14
-rw-r--r--src/qt/optionsdialog.cpp2
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/peertablemodel.cpp1
-rw-r--r--src/qt/peertablemodel.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp10
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/rpcnestedtests.cpp2
-rw-r--r--src/qt/transactiondesc.cpp32
-rw-r--r--src/qt/transactionrecord.cpp20
-rw-r--r--src/qt/transactiontablemodel.cpp2
-rw-r--r--src/qt/walletmodel.cpp16
-rw-r--r--src/qt/walletmodeltransaction.cpp4
-rw-r--r--src/rest.cpp6
-rw-r--r--src/rpc/blockchain.cpp6
-rw-r--r--src/rpc/mining.cpp12
-rw-r--r--src/rpc/misc.cpp2
-rw-r--r--src/rpc/net.cpp3
-rw-r--r--src/rpc/rawtransaction.cpp21
-rw-r--r--src/script/bitcoinconsensus.cpp3
-rw-r--r--src/test/DoS_tests.cpp2
-rw-r--r--src/test/bloom_tests.cpp26
-rw-r--r--src/test/coins_tests.cpp2
-rw-r--r--src/test/main_tests.cpp3
-rw-r--r--src/test/miner_tests.cpp5
-rw-r--r--src/test/policyestimator_tests.cpp22
-rw-r--r--src/test/script_P2SH_tests.cpp2
-rw-r--r--src/test/script_tests.cpp12
-rw-r--r--src/test/serialize_tests.cpp6
-rw-r--r--src/test/sighash_tests.cpp8
-rw-r--r--src/test/sigopcount_tests.cpp2
-rw-r--r--src/test/test_bitcoin.cpp6
-rw-r--r--src/test/transaction_tests.cpp52
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/test/versionbits_tests.cpp2
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/validation.cpp (renamed from src/main.cpp)3065
-rw-r--r--src/validation.h (renamed from src/main.h)60
-rw-r--r--src/wallet/rpcdump.cpp8
-rw-r--r--src/wallet/rpcwallet.cpp29
-rw-r--r--src/wallet/test/accounting_tests.cpp4
-rw-r--r--src/wallet/test/wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp112
-rw-r--r--src/wallet/wallet.h33
-rw-r--r--src/wallet/walletdb.cpp2
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
-rw-r--r--src/zmq/zmqpublishnotifier.cpp3
75 files changed, 3563 insertions, 3507 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 69ae02f582..8c12aee217 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,11 +104,11 @@ BITCOIN_CORE_H = \
keystore.h \
dbwrapper.h \
limitedmap.h \
- main.h \
memusage.h \
merkleblock.h \
miner.h \
net.h \
+ net_processing.h \
netaddress.h \
netbase.h \
netmessagemaker.h \
@@ -145,6 +145,7 @@ BITCOIN_CORE_H = \
util.h \
utilmoneystr.h \
utiltime.h \
+ validation.h \
validationinterface.h \
versionbits.h \
wallet/coincontrol.h \
@@ -179,10 +180,10 @@ libbitcoin_server_a_SOURCES = \
httpserver.cpp \
init.cpp \
dbwrapper.cpp \
- main.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
+ net_processing.cpp \
noui.cpp \
policy/fees.cpp \
policy/policy.cpp \
@@ -201,6 +202,7 @@ libbitcoin_server_a_SOURCES = \
txdb.cpp \
txmempool.cpp \
ui_interface.cpp \
+ validation.cpp \
validationinterface.cpp \
versionbits.cpp \
$(BITCOIN_CORE_H)
diff --git a/src/bench/Examples.cpp b/src/bench/Examples.cpp
index b6b020a971..9f35a1ea04 100644
--- a/src/bench/Examples.cpp
+++ b/src/bench/Examples.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "bench.h"
-#include "main.h"
+#include "validation.h"
#include "utiltime.h"
// Sanity test: this should loop ten times, and
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index a791b5b7fa..3319c179bf 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -4,7 +4,7 @@
#include "bench.h"
-#include "main.h"
+#include "validation.h"
#include "base58.h"
#include <vector>
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index db1402216d..bd768180c6 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -5,7 +5,7 @@
#include "bench.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
int
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 4a564d3fc8..230e4ca773 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -5,7 +5,8 @@
#include "bench.h"
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
+#include "streams.h"
#include "consensus/validation.h"
namespace block_bench {
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 7091ee3e11..32690fe484 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -19,7 +19,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, vector<COutput
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- CWalletTx* wtx = new CWalletTx(&wallet, tx);
+ CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
int nAge = 6 * 24;
COutput output(wtx, nInput, nAge, true, true);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index b272640819..596cc8eada 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -247,7 +247,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params)
event_base_free(base);
if (response.status == 0)
- throw CConnectionFailed(strprintf("couldn't connect to server\n(make sure server is running and you are connecting to the correct RPC port: %d %s)", response.error, http_errorstring(response.error)));
+ throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
else if (response.status == HTTP_UNAUTHORIZED)
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 346d8e180d..1ed1449f03 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -623,7 +623,7 @@ static int CommandLineRawTx(int argc, char* argv[])
argv++;
}
- CTransaction txDecodeTmp;
+ CMutableTransaction tx;
int startArg;
if (!fCreateBlank) {
@@ -636,15 +636,13 @@ static int CommandLineRawTx(int argc, char* argv[])
if (strHexTx == "-") // "-" implies standard input
strHexTx = readStdin();
- if (!DecodeHexTx(txDecodeTmp, strHexTx, true))
+ if (!DecodeHexTx(tx, strHexTx, true))
throw std::runtime_error("invalid transaction encoding");
startArg = 2;
} else
startArg = 1;
- CMutableTransaction tx(txDecodeTmp);
-
for (int i = startArg; i < argc; i++) {
std::string arg = argv[i];
std::string key, value;
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 2efeda9cc6..914af0c670 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -10,7 +10,7 @@
#include "random.h"
#include "streams.h"
#include "txmempool.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
#include <unordered_map>
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index d22c188c16..a487fb1dc4 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -6,7 +6,7 @@
#include "chain.h"
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
#include "uint256.h"
#include <stdint.h>
diff --git a/src/core_io.h b/src/core_io.h
index 5aecbc4489..4344290bb8 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -11,13 +11,14 @@
class CBlock;
class CScript;
class CTransaction;
+class CMutableTransaction;
class uint256;
class UniValue;
// core_read.cpp
CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
-bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
+bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
uint256 ParseHashUV(const UniValue& v, const std::string& strName);
uint256 ParseHashStr(const std::string&, const std::string& strName);
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 7cfda6dd6d..b6d0285459 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -90,7 +90,7 @@ CScript ParseScript(const std::string& s)
return result;
}
-bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
+bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
{
if (!IsHex(strHexTx))
return false;
diff --git a/src/init.cpp b/src/init.cpp
index 377d196f2b..ba5fe4152a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -19,10 +19,11 @@
#include "httpserver.h"
#include "httprpc.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "netbase.h"
#include "net.h"
+#include "net_processing.h"
#include "policy/policy.h"
#include "rpc/server.h"
#include "rpc/register.h"
@@ -391,7 +392,7 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
#endif
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
- strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
+ strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY));
diff --git a/src/miner.cpp b/src/miner.cpp
index c40b12cd8e..e80e8a2656 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -13,7 +13,7 @@
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/policy.h"
#include "pow.h"
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
new file mode 100644
index 0000000000..c37faf49f0
--- /dev/null
+++ b/src/net_processing.cpp
@@ -0,0 +1,3025 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-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 "net_processing.h"
+
+#include "addrman.h"
+#include "arith_uint256.h"
+#include "blockencodings.h"
+#include "chainparams.h"
+#include "consensus/validation.h"
+#include "hash.h"
+#include "init.h"
+#include "validation.h"
+#include "merkleblock.h"
+#include "net.h"
+#include "netmessagemaker.h"
+#include "netbase.h"
+#include "policy/fees.h"
+#include "policy/policy.h"
+#include "primitives/block.h"
+#include "primitives/transaction.h"
+#include "random.h"
+#include "tinyformat.h"
+#include "txmempool.h"
+#include "ui_interface.h"
+#include "util.h"
+#include "utilmoneystr.h"
+#include "utilstrencodings.h"
+#include "validationinterface.h"
+
+#include <boost/thread.hpp>
+
+using namespace std;
+
+#if defined(NDEBUG)
+# error "Bitcoin cannot be compiled without assertions."
+#endif
+
+int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last received a block
+
+struct IteratorComparator
+{
+ template<typename I>
+ bool operator()(const I& a, const I& b)
+ {
+ return &(*a) < &(*b);
+ }
+};
+
+struct COrphanTx {
+ CTransaction tx;
+ NodeId fromPeer;
+ int64_t nTimeExpire;
+};
+map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
+map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
+void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+
+// Internal stuff
+namespace {
+ /** Number of nodes with fSyncStarted. */
+ int nSyncStarted = 0;
+
+ /**
+ * Sources of received blocks, saved to be able to send them reject
+ * messages or ban them when processing happens afterwards. Protected by
+ * cs_main.
+ * Set mapBlockSource[hash].second to false if the node should not be
+ * punished if the block is invalid.
+ */
+ map<uint256, std::pair<NodeId, bool>> mapBlockSource;
+
+ /**
+ * Filter for transactions that were recently rejected by
+ * AcceptToMemoryPool. These are not rerequested until the chain tip
+ * changes, at which point the entire filter is reset. Protected by
+ * cs_main.
+ *
+ * Without this filter we'd be re-requesting txs from each of our peers,
+ * increasing bandwidth consumption considerably. For instance, with 100
+ * peers, half of which relay a tx we don't accept, that might be a 50x
+ * bandwidth increase. A flooding attacker attempting to roll-over the
+ * filter using minimum-sized, 60byte, transactions might manage to send
+ * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
+ * two minute window to send invs to us.
+ *
+ * Decreasing the false positive rate is fairly cheap, so we pick one in a
+ * million to make it highly unlikely for users to have issues with this
+ * filter.
+ *
+ * Memory used: 1.3 MB
+ */
+ std::unique_ptr<CRollingBloomFilter> recentRejects;
+ uint256 hashRecentRejectsChainTip;
+
+ /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
+ struct QueuedBlock {
+ uint256 hash;
+ CBlockIndex* pindex; //!< Optional.
+ bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ };
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
+
+ /** Stack of nodes which we have set to announce using compact blocks */
+ list<NodeId> lNodesAnnouncingHeaderAndIDs;
+
+ /** Number of preferable block download peers. */
+ int nPreferredDownload = 0;
+
+ /** Number of peers from which we're downloading blocks. */
+ int nPeersWithValidatedDownloads = 0;
+
+ /** Relay map, protected by cs_main. */
+ typedef std::map<uint256, CTransactionRef> MapRelay;
+ MapRelay mapRelay;
+ /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
+ std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
+} // anon namespace
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Registration of network node signals.
+//
+
+namespace {
+
+struct CBlockReject {
+ unsigned char chRejectCode;
+ string strRejectReason;
+ uint256 hashBlock;
+};
+
+/**
+ * Maintain validation-specific state about nodes, protected by cs_main, instead
+ * by CNode's own locks. This simplifies asynchronous operation, where
+ * processing of incoming data is done after the ProcessMessage call returns,
+ * and we're no longer holding the node's locks.
+ */
+struct CNodeState {
+ //! The peer's address
+ const CService address;
+ //! Whether we have a fully established connection.
+ bool fCurrentlyConnected;
+ //! Accumulated misbehaviour score for this peer.
+ int nMisbehavior;
+ //! Whether this peer should be disconnected and banned (unless whitelisted).
+ bool fShouldBan;
+ //! String name of this peer (debugging/logging purposes).
+ const std::string name;
+ //! List of asynchronously-determined block rejections to notify this peer about.
+ std::vector<CBlockReject> rejects;
+ //! The best known block we know this peer has announced.
+ CBlockIndex *pindexBestKnownBlock;
+ //! The hash of the last unknown block this peer has announced.
+ uint256 hashLastUnknownBlock;
+ //! The last full block we both have.
+ CBlockIndex *pindexLastCommonBlock;
+ //! The best header we have sent our peer.
+ CBlockIndex *pindexBestHeaderSent;
+ //! Length of current-streak of unconnecting headers announcements
+ int nUnconnectingHeaders;
+ //! Whether we've started headers synchronization with this peer.
+ bool fSyncStarted;
+ //! Since when we're stalling block download progress (in microseconds), or 0.
+ int64_t nStallingSince;
+ list<QueuedBlock> vBlocksInFlight;
+ //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
+ int64_t nDownloadingSince;
+ int nBlocksInFlight;
+ int nBlocksInFlightValidHeaders;
+ //! Whether we consider this a preferred download peer.
+ bool fPreferredDownload;
+ //! Whether this peer wants invs or headers (when possible) for block announcements.
+ bool fPreferHeaders;
+ //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
+ bool fPreferHeaderAndIDs;
+ /**
+ * Whether this peer will send us cmpctblocks if we request them.
+ * This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
+ * but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
+ */
+ bool fProvidesHeaderAndIDs;
+ //! Whether this peer can give us witnesses
+ bool fHaveWitness;
+ //! Whether this peer wants witnesses in cmpctblocks/blocktxns
+ bool fWantsCmpctWitness;
+ /**
+ * If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
+ * otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
+ */
+ bool fSupportsDesiredCmpctVersion;
+
+ CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
+ fCurrentlyConnected = false;
+ nMisbehavior = 0;
+ fShouldBan = false;
+ pindexBestKnownBlock = NULL;
+ hashLastUnknownBlock.SetNull();
+ pindexLastCommonBlock = NULL;
+ pindexBestHeaderSent = NULL;
+ nUnconnectingHeaders = 0;
+ fSyncStarted = false;
+ nStallingSince = 0;
+ nDownloadingSince = 0;
+ nBlocksInFlight = 0;
+ nBlocksInFlightValidHeaders = 0;
+ fPreferredDownload = false;
+ fPreferHeaders = false;
+ fPreferHeaderAndIDs = false;
+ fProvidesHeaderAndIDs = false;
+ fHaveWitness = false;
+ fWantsCmpctWitness = false;
+ fSupportsDesiredCmpctVersion = false;
+ }
+};
+
+/** Map maintaining per-node state. Requires cs_main. */
+map<NodeId, CNodeState> mapNodeState;
+
+// Requires cs_main.
+CNodeState *State(NodeId pnode) {
+ map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
+ if (it == mapNodeState.end())
+ return NULL;
+ return &it->second;
+}
+
+void UpdatePreferredDownload(CNode* node, CNodeState* state)
+{
+ nPreferredDownload -= state->fPreferredDownload;
+
+ // Whether this node should be marked as a preferred download node.
+ state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
+
+ nPreferredDownload += state->fPreferredDownload;
+}
+
+void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
+{
+ ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
+ uint64_t nonce = pnode->GetLocalNonce();
+ int nNodeStartingHeight = pnode->GetMyStartingHeight();
+ NodeId nodeid = pnode->GetId();
+ CAddress addr = pnode->addr;
+
+ CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
+ CAddress addrMe = CAddress(CService(), nLocalNodeServices);
+
+ connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
+ nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
+
+ if (fLogIPs)
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
+ else
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), nodeid);
+}
+
+void InitializeNode(CNode *pnode, CConnman& connman) {
+ CAddress addr = pnode->addr;
+ std::string addrName = pnode->addrName;
+ NodeId nodeid = pnode->GetId();
+ {
+ LOCK(cs_main);
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName)));
+ }
+ if(!pnode->fInbound)
+ PushNodeVersion(pnode, connman, GetTime());
+}
+
+void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+ fUpdateConnectionTime = false;
+ LOCK(cs_main);
+ CNodeState *state = State(nodeid);
+
+ if (state->fSyncStarted)
+ nSyncStarted--;
+
+ if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ fUpdateConnectionTime = true;
+ }
+
+ BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) {
+ mapBlocksInFlight.erase(entry.hash);
+ }
+ EraseOrphansFor(nodeid);
+ nPreferredDownload -= state->fPreferredDownload;
+ nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
+ assert(nPeersWithValidatedDownloads >= 0);
+
+ mapNodeState.erase(nodeid);
+
+ if (mapNodeState.empty()) {
+ // Do a consistency check after the last peer is removed.
+ assert(mapBlocksInFlight.empty());
+ assert(nPreferredDownload == 0);
+ assert(nPeersWithValidatedDownloads == 0);
+ }
+}
+
+// Requires cs_main.
+// Returns a bool indicating whether we requested this block.
+// Also used if a block was /not/ received and timed out or started with another peer
+bool MarkBlockAsReceived(const uint256& hash) {
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
+ if (itInFlight != mapBlocksInFlight.end()) {
+ CNodeState *state = State(itInFlight->second.first);
+ state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
+ if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
+ // Last validated block on the queue was received.
+ nPeersWithValidatedDownloads--;
+ }
+ if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
+ // First block on the queue was received, update the start download time for the next one
+ state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros());
+ }
+ state->vBlocksInFlight.erase(itInFlight->second.second);
+ state->nBlocksInFlight--;
+ state->nStallingSince = 0;
+ mapBlocksInFlight.erase(itInFlight);
+ return true;
+ }
+ return false;
+}
+
+// Requires cs_main.
+// returns false, still setting pit, if the block was already in flight from the same peer
+// pit will only be valid as long as the same cs_main lock is being held
+bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ // Short-circuit most stuff in case its from the same node
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
+ if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) {
+ *pit = &itInFlight->second.second;
+ return false;
+ }
+
+ // Make sure it's not listed somewhere already.
+ MarkBlockAsReceived(hash);
+
+ list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
+ {hash, pindex, pindex != NULL, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)});
+ state->nBlocksInFlight++;
+ state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
+ if (state->nBlocksInFlight == 1) {
+ // We're starting a block download (batch) from this peer.
+ state->nDownloadingSince = GetTimeMicros();
+ }
+ if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) {
+ nPeersWithValidatedDownloads++;
+ }
+ itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
+ if (pit)
+ *pit = &itInFlight->second.second;
+ return true;
+}
+
+/** Check whether the last unknown block a peer advertised is not yet known. */
+void ProcessBlockAvailability(NodeId nodeid) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ if (!state->hashLastUnknownBlock.IsNull()) {
+ BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
+ if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
+ if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
+ state->pindexBestKnownBlock = itOld->second;
+ state->hashLastUnknownBlock.SetNull();
+ }
+ }
+}
+
+/** Update tracking information about which blocks a peer is assumed to have. */
+void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ ProcessBlockAvailability(nodeid);
+
+ BlockMap::iterator it = mapBlockIndex.find(hash);
+ if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
+ // An actually better block was announced.
+ if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
+ state->pindexBestKnownBlock = it->second;
+ } else {
+ // An unknown block was announced; just assume that the latest one is the best one.
+ state->hashLastUnknownBlock = hash;
+ }
+}
+
+void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) {
+ if (!nodestate->fSupportsDesiredCmpctVersion) {
+ // Never ask from peers who can't provide witnesses.
+ return;
+ }
+ if (nodestate->fProvidesHeaderAndIDs) {
+ for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
+ if (*it == pfrom->GetId()) {
+ lNodesAnnouncingHeaderAndIDs.erase(it);
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ return;
+ }
+ }
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
+ // As per BIP152, we only get 3 of our peers to announce
+ // blocks using compact encodings.
+ connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ return true;
+ });
+ lNodesAnnouncingHeaderAndIDs.pop_front();
+ }
+ fAnnounceUsingCMPCTBLOCK = true;
+ connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ }
+}
+
+// Requires cs_main
+bool CanDirectFetch(const Consensus::Params &consensusParams)
+{
+ return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+}
+
+// Requires cs_main
+bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
+{
+ if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
+ return true;
+ if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
+ return true;
+ return false;
+}
+
+/** Find the last common ancestor two blocks have.
+ * Both pa and pb must be non-NULL. */
+CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
+ if (pa->nHeight > pb->nHeight) {
+ pa = pa->GetAncestor(pb->nHeight);
+ } else if (pb->nHeight > pa->nHeight) {
+ pb = pb->GetAncestor(pa->nHeight);
+ }
+
+ while (pa != pb && pa && pb) {
+ pa = pa->pprev;
+ pb = pb->pprev;
+ }
+
+ // Eventually all chain branches meet at the genesis block.
+ assert(pa == pb);
+ return pa;
+}
+
+/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
+ * at most count entries. */
+void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
+ if (count == 0)
+ return;
+
+ vBlocks.reserve(vBlocks.size() + count);
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ // Make sure pindexBestKnownBlock is up to date, we'll need it.
+ ProcessBlockAvailability(nodeid);
+
+ if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
+ // This peer has nothing interesting.
+ return;
+ }
+
+ if (state->pindexLastCommonBlock == NULL) {
+ // Bootstrap quickly by guessing a parent of our best tip is the forking point.
+ // Guessing wrong in either direction is not a problem.
+ state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
+ }
+
+ // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
+ // of its current tip anymore. Go back enough to fix that.
+ state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
+ if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
+ return;
+
+ std::vector<CBlockIndex*> vToFetch;
+ CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
+ // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
+ // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
+ // download that next block if the window were 1 larger.
+ int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
+ int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
+ NodeId waitingfor = -1;
+ while (pindexWalk->nHeight < nMaxHeight) {
+ // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
+ // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
+ // as iterating over ~100 CBlockIndex* entries anyway.
+ int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
+ vToFetch.resize(nToFetch);
+ pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
+ vToFetch[nToFetch - 1] = pindexWalk;
+ for (unsigned int i = nToFetch - 1; i > 0; i--) {
+ vToFetch[i - 1] = vToFetch[i]->pprev;
+ }
+
+ // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
+ // are not yet downloaded and not in flight to vBlocks. In the mean time, update
+ // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
+ // already part of our chain (and therefore don't need it even if pruned).
+ BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
+ if (!pindex->IsValid(BLOCK_VALID_TREE)) {
+ // We consider the chain that this peer is on invalid.
+ return;
+ }
+ if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
+ // We wouldn't download this block or its descendants from this peer.
+ return;
+ }
+ if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
+ if (pindex->nChainTx)
+ state->pindexLastCommonBlock = pindex;
+ } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ // The block is not already downloaded, and not yet in flight.
+ if (pindex->nHeight > nWindowEnd) {
+ // We reached the end of the window.
+ if (vBlocks.size() == 0 && waitingfor != nodeid) {
+ // We aren't able to fetch anything, but we would be if the download window was one larger.
+ nodeStaller = waitingfor;
+ }
+ return;
+ }
+ vBlocks.push_back(pindex);
+ if (vBlocks.size() == count) {
+ return;
+ }
+ } else if (waitingfor == -1) {
+ // This is the first already-in-flight block.
+ waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
+ }
+ }
+ }
+}
+
+} // anon namespace
+
+bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
+ LOCK(cs_main);
+ CNodeState *state = State(nodeid);
+ if (state == NULL)
+ return false;
+ stats.nMisbehavior = state->nMisbehavior;
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
+ BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) {
+ if (queue.pindex)
+ stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ }
+ return true;
+}
+
+void RegisterNodeSignals(CNodeSignals& nodeSignals)
+{
+ nodeSignals.ProcessMessages.connect(&ProcessMessages);
+ nodeSignals.SendMessages.connect(&SendMessages);
+ nodeSignals.InitializeNode.connect(&InitializeNode);
+ nodeSignals.FinalizeNode.connect(&FinalizeNode);
+}
+
+void UnregisterNodeSignals(CNodeSignals& nodeSignals)
+{
+ nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
+ nodeSignals.SendMessages.disconnect(&SendMessages);
+ nodeSignals.InitializeNode.disconnect(&InitializeNode);
+ nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// mapOrphanTransactions
+//
+
+bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ uint256 hash = tx.GetHash();
+ if (mapOrphanTransactions.count(hash))
+ return false;
+
+ // Ignore big transactions, to avoid a
+ // send-big-orphans memory exhaustion attack. If a peer has a legitimate
+ // large transaction with a missing parent then we assume
+ // it will rebroadcast it later, after the parent transaction(s)
+ // have been mined or received.
+ // 100 orphans, each of which is at most 99,999 bytes big is
+ // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
+ unsigned int sz = GetTransactionWeight(tx);
+ if (sz >= MAX_STANDARD_TX_WEIGHT)
+ {
+ LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
+ return false;
+ }
+
+ auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
+ assert(ret.second);
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
+ }
+
+ LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
+ mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
+ return true;
+}
+
+int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
+ if (it == mapOrphanTransactions.end())
+ return 0;
+ BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
+ {
+ auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
+ if (itPrev == mapOrphanTransactionsByPrev.end())
+ continue;
+ itPrev->second.erase(it);
+ if (itPrev->second.empty())
+ mapOrphanTransactionsByPrev.erase(itPrev);
+ }
+ mapOrphanTransactions.erase(it);
+ return 1;
+}
+
+void EraseOrphansFor(NodeId peer)
+{
+ int nErased = 0;
+ map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
+ while (iter != mapOrphanTransactions.end())
+ {
+ map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
+ if (maybeErase->second.fromPeer == peer)
+ {
+ nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
+ }
+ }
+ if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
+}
+
+
+unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ unsigned int nEvicted = 0;
+ static int64_t nNextSweep;
+ int64_t nNow = GetTime();
+ if (nNextSweep <= nNow) {
+ // Sweep out expired orphan pool entries:
+ int nErased = 0;
+ int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
+ map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
+ while (iter != mapOrphanTransactions.end())
+ {
+ map<uint256, COrphanTx>::iterator maybeErase = iter++;
+ if (maybeErase->second.nTimeExpire <= nNow) {
+ nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
+ } else {
+ nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
+ }
+ }
+ // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
+ nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
+ if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased);
+ }
+ while (mapOrphanTransactions.size() > nMaxOrphans)
+ {
+ // Evict a random orphan:
+ uint256 randomhash = GetRandHash();
+ map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ if (it == mapOrphanTransactions.end())
+ it = mapOrphanTransactions.begin();
+ EraseOrphanTx(it->first);
+ ++nEvicted;
+ }
+ return nEvicted;
+}
+
+// Requires cs_main.
+void Misbehaving(NodeId pnode, int howmuch)
+{
+ if (howmuch == 0)
+ return;
+
+ CNodeState *state = State(pnode);
+ if (state == NULL)
+ return;
+
+ state->nMisbehavior += howmuch;
+ int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
+ if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
+ {
+ LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
+ state->fShouldBan = true;
+ } else
+ LogPrintf("%s: %s peer=%d (%d -> %d)\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
+}
+
+
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// blockchain -> download logic notification
+//
+
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
+ // Initialize global variables that cannot be constructed at startup.
+ recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
+}
+
+void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
+ if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
+ return;
+
+ LOCK(cs_main);
+
+ std::vector<uint256> vOrphanErase;
+ // Which orphan pool entries must we evict?
+ for (size_t j = 0; j < tx.vin.size(); j++) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
+ if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = (*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
+ }
+
+ // Erase orphan transactions include or precluded by this block
+ if (vOrphanErase.size()) {
+ int nErased = 0;
+ BOOST_FOREACH(uint256 &orphanHash, vOrphanErase) {
+ nErased += EraseOrphanTx(orphanHash);
+ }
+ LogPrint("mempool", "Erased %d orphan tx included or conflicted by block\n", nErased);
+ }
+}
+
+void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+ const int nNewHeight = pindexNew->nHeight;
+ connman->SetBestHeight(nNewHeight);
+
+ if (!fInitialDownload) {
+ // Find the hashes of all blocks that weren't previously in the best chain.
+ std::vector<uint256> vHashes;
+ const CBlockIndex *pindexToAnnounce = pindexNew;
+ while (pindexToAnnounce != pindexFork) {
+ vHashes.push_back(pindexToAnnounce->GetBlockHash());
+ pindexToAnnounce = pindexToAnnounce->pprev;
+ if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
+ // Limit announcements in case of a huge reorganization.
+ // Rely on the peer's synchronization mechanism in that case.
+ break;
+ }
+ }
+ // Relay inventory, but don't relay old inventory during initial block download.
+ connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
+ if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
+ BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
+ pnode->PushBlockHash(hash);
+ }
+ }
+ });
+ }
+
+ nTimeBestReceived = GetTime();
+}
+
+void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
+ LOCK(cs_main);
+
+ const uint256 hash(block.GetHash());
+ std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
+
+ int nDoS = 0;
+ if (state.IsInvalid(nDoS)) {
+ if (it != mapBlockSource.end() && State(it->second.first)) {
+ assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
+ CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
+ State(it->second.first)->rejects.push_back(reject);
+ if (nDoS > 0 && it->second.second)
+ Misbehaving(it->second.first, nDoS);
+ }
+ }
+ if (it != mapBlockSource.end())
+ mapBlockSource.erase(it);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Messages
+//
+
+
+bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ switch (inv.type)
+ {
+ case MSG_TX:
+ case MSG_WITNESS_TX:
+ {
+ assert(recentRejects);
+ if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
+ {
+ // If the chain tip has changed previously rejected transactions
+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
+ // or a double-spend. Reset the rejects filter and give those
+ // txs a second chance.
+ hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
+ recentRejects->reset();
+ }
+
+ // Use pcoinsTip->HaveCoinsInCache as a quick approximation to exclude
+ // requesting or processing some txs which have already been included in a block
+ return recentRejects->contains(inv.hash) ||
+ mempool.exists(inv.hash) ||
+ mapOrphanTransactions.count(inv.hash) ||
+ pcoinsTip->HaveCoinsInCache(inv.hash);
+ }
+ case MSG_BLOCK:
+ case MSG_WITNESS_BLOCK:
+ return mapBlockIndex.count(inv.hash);
+ }
+ // Don't know what it is, just say we already got one
+ return true;
+}
+
+static void RelayTransaction(const CTransaction& tx, CConnman& connman)
+{
+ CInv inv(MSG_TX, tx.GetHash());
+ connman.ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
+}
+
+static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
+{
+ unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
+
+ // Relay to a limited number of other nodes
+ // Use deterministic randomness to send to the same nodes for 24 hours
+ // at a time so the addrKnowns of the chosen nodes prevent repeats
+ uint64_t hashAddr = addr.GetHash();
+ const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
+ FastRandomContext insecure_rand;
+
+ std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}};
+ assert(nRelayNodes <= best.size());
+
+ auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
+ if (pnode->nVersion >= CADDR_TIME_VERSION) {
+ uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
+ for (unsigned int i = 0; i < nRelayNodes; i++) {
+ if (hashKey > best[i].first) {
+ std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);
+ best[i] = std::make_pair(hashKey, pnode);
+ break;
+ }
+ }
+ }
+ };
+
+ auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] {
+ for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
+ best[i].second->PushAddress(addr, insecure_rand);
+ }
+ };
+
+ connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+}
+
+void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman)
+{
+ std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+ vector<CInv> vNotFound;
+ CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ LOCK(cs_main);
+
+ while (it != pfrom->vRecvGetData.end()) {
+ // Don't bother if send buffer is too full to respond anyway
+ if (pfrom->nSendSize >= nMaxSendBufferSize)
+ break;
+
+ const CInv &inv = *it;
+ {
+ boost::this_thread::interruption_point();
+ it++;
+
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
+ {
+ bool send = false;
+ BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
+ if (mi != mapBlockIndex.end())
+ {
+ if (chainActive.Contains(mi->second)) {
+ send = true;
+ } else {
+ static const int nOneMonth = 30 * 24 * 60 * 60;
+ // To prevent fingerprinting attacks, only send blocks outside of the active
+ // chain if they are valid, and no more than a month older (both in time, and in
+ // best equivalent proof of work) than the best header chain we know about.
+ send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
+ (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
+ if (!send) {
+ LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
+ }
+ }
+ }
+ // disconnect node in case we have reached the outbound limit for serving historical blocks
+ // never disconnect whitelisted nodes
+ static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
+ if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+
+ //disconnect node
+ pfrom->fDisconnect = true;
+ send = false;
+ }
+ // Pruned nodes may have deleted the block, so check whether
+ // it's available before trying to send.
+ if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
+ {
+ // Send block from disk
+ CBlock block;
+ if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
+ assert(!"cannot load block from disk");
+ if (inv.type == MSG_BLOCK)
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block));
+ else if (inv.type == MSG_WITNESS_BLOCK)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block));
+ else if (inv.type == MSG_FILTERED_BLOCK)
+ {
+ bool sendMerkleBlock = false;
+ CMerkleBlock merkleBlock;
+ {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter) {
+ sendMerkleBlock = true;
+ merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
+ }
+ }
+ if (sendMerkleBlock) {
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
+ // This avoids hurting performance by pointlessly requiring a round-trip
+ // Note that there is currently no way for a node to request any single transactions we didn't send here -
+ // they must either disconnect and retry or request the full block.
+ // Thus, the protocol spec specified allows for us to provide duplicate txn here,
+ // however we MUST always provide at least what the remote peer needs
+ typedef std::pair<unsigned int, uint256> PairType;
+ BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]));
+ }
+ // else
+ // no response
+ }
+ else if (inv.type == MSG_CMPCT_BLOCK)
+ {
+ // If a peer is asking for old blocks, we're almost guaranteed
+ // they won't have a useful mempool to match against a compact block,
+ // and we don't feel like constructing the object for them, so
+ // instead we respond with the full, non-compact block.
+ bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
+ int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
+ CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ } else
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block));
+ }
+
+ // Trigger the peer node to send a getblocks request for the next batch of inventory
+ if (inv.hash == pfrom->hashContinue)
+ {
+ // Bypass PushInventory, this must send even if redundant,
+ // and we want it right after the last block so they don't
+ // wait for other stuff first.
+ vector<CInv> vInv;
+ vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ pfrom->hashContinue.SetNull();
+ }
+ }
+ }
+ else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
+ {
+ // Send stream from relay memory
+ bool push = false;
+ auto mi = mapRelay.find(inv.hash);
+ int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
+ if (mi != mapRelay.end()) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
+ push = true;
+ } else if (pfrom->timeLastMempoolReq) {
+ auto txinfo = mempool.info(inv.hash);
+ // To protect privacy, do not answer getdata using the mempool when
+ // that TX couldn't have been INVed in reply to a MEMPOOL request.
+ if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
+ push = true;
+ }
+ }
+ if (!push) {
+ vNotFound.push_back(inv);
+ }
+ }
+
+ // Track requests for our stuff.
+ GetMainSignals().Inventory(inv.hash);
+
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
+ break;
+ }
+ }
+
+ pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
+
+ if (!vNotFound.empty()) {
+ // Let the peer know that we didn't find what it asked for, so it doesn't
+ // have to wait around forever. Currently only SPV clients actually care
+ // about this message: it's needed when they are recursively walking the
+ // dependencies of relevant unconfirmed transactions. SPV clients want to
+ // do that because they want to know about (and store and rebroadcast and
+ // risk analyze) the dependencies of transactions relevant to them, without
+ // having to download the entire memory pool.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ }
+}
+
+uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) {
+ uint32_t nFetchFlags = 0;
+ if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
+ nFetchFlags |= MSG_WITNESS_FLAG;
+ }
+ return nFetchFlags;
+}
+
+bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman)
+{
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+
+ LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
+ if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
+ {
+ LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
+ return true;
+ }
+
+
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
+ (strCommand == NetMsgType::FILTERLOAD ||
+ strCommand == NetMsgType::FILTERADD))
+ {
+ if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ return false;
+ } else {
+ pfrom->fDisconnect = true;
+ return false;
+ }
+ }
+
+
+ if (strCommand == NetMsgType::VERSION)
+ {
+ // Each connection can only send one version message
+ if (pfrom->nVersion != 0)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")));
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 1);
+ return false;
+ }
+
+ int64_t nTime;
+ CAddress addrMe;
+ CAddress addrFrom;
+ uint64_t nNonce = 1;
+ uint64_t nServiceInt;
+ vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
+ pfrom->nServices = ServiceFlags(nServiceInt);
+ if (!pfrom->fInbound)
+ {
+ connman.SetServices(pfrom->addr, pfrom->nServices);
+ }
+ if (pfrom->nServicesExpected & ~pfrom->nServices)
+ {
+ LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
+ strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
+ pfrom->fDisconnect = true;
+ return false;
+ }
+
+ if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
+ {
+ // disconnect from peers older than this proto version
+ LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
+ strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
+ pfrom->fDisconnect = true;
+ return false;
+ }
+
+ if (pfrom->nVersion == 10300)
+ pfrom->nVersion = 300;
+ if (!vRecv.empty())
+ vRecv >> addrFrom >> nNonce;
+ if (!vRecv.empty()) {
+ vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
+ pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
+ }
+ if (!vRecv.empty()) {
+ vRecv >> pfrom->nStartingHeight;
+ }
+ {
+ LOCK(pfrom->cs_filter);
+ if (!vRecv.empty())
+ vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
+ else
+ pfrom->fRelayTxes = true;
+ }
+
+ // Disconnect if we connected to ourself
+ if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
+ {
+ LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ pfrom->addrLocal = addrMe;
+ if (pfrom->fInbound && addrMe.IsRoutable())
+ {
+ SeenLocal(addrMe);
+ }
+
+ // Be shy and don't send version until we hear
+ if (pfrom->fInbound)
+ PushNodeVersion(pfrom, connman, GetAdjustedTime());
+
+ pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
+
+ if((pfrom->nServices & NODE_WITNESS))
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fHaveWitness = true;
+ }
+
+ // Potentially mark this peer as a preferred download peer.
+ {
+ LOCK(cs_main);
+ UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
+ }
+
+ // Change version
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION);
+ pfrom->SetSendVersion(nSendVersion);
+
+ if (!pfrom->fInbound)
+ {
+ // Advertise our address
+ if (fListen && !IsInitialBlockDownload())
+ {
+ CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
+ FastRandomContext insecure_rand;
+ if (addr.IsRoutable())
+ {
+ LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
+ pfrom->PushAddress(addr, insecure_rand);
+ } else if (IsPeerAddrLocalGood(pfrom)) {
+ addr.SetIP(pfrom->addrLocal);
+ LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+ }
+
+ // Get recent addresses
+ if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom->fGetAddr = true;
+ }
+ connman.MarkAddressGood(pfrom->addr);
+ }
+
+ pfrom->fSuccessfullyConnected = true;
+
+ string remoteAddr;
+ if (fLogIPs)
+ remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
+
+ LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
+ pfrom->cleanSubVer, pfrom->nVersion,
+ pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
+ remoteAddr);
+
+ int64_t nTimeOffset = nTime - GetTime();
+ pfrom->nTimeOffset = nTimeOffset;
+ AddTimeData(pfrom->addr, nTimeOffset);
+
+ // Feeler connections exist only to verify if address is online.
+ if (pfrom->fFeeler) {
+ assert(pfrom->fInbound == false);
+ pfrom->fDisconnect = true;
+ }
+ return true;
+ }
+
+
+ else if (pfrom->nVersion == 0)
+ {
+ // Must have a version message before anything else
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 1);
+ return false;
+ }
+
+ // At this point, the outgoing message serialization version can't change.
+ CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+
+ if (strCommand == NetMsgType::VERACK)
+ {
+ pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
+
+ if (!pfrom->fInbound) {
+ // Mark this node as currently connected, so we update its timestamp later.
+ LOCK(cs_main);
+ State(pfrom->GetId())->fCurrentlyConnected = true;
+ }
+
+ if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ // Tell our peer we prefer to receive headers rather than inv's
+ // We send this to non-NODE NETWORK peers as well, because even
+ // non-NODE NETWORK peers can announce blocks (such as pruning
+ // nodes)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ }
+ if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ // Tell our peer we are willing to provide version 1 or 2 cmpctblocks
+ // However, we do not request new block announcements using
+ // cmpctblock messages.
+ // We send this to non-NODE NETWORK peers as well, because
+ // they may wish to request compact blocks from us
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 2;
+ if (pfrom->GetLocalServices() & NODE_WITNESS)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ nCMPCTBLOCKVersion = 1;
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::ADDR)
+ {
+ vector<CAddress> vAddr;
+ vRecv >> vAddr;
+
+ // Don't want addr from older versions unless seeding
+ if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
+ return true;
+ if (vAddr.size() > 1000)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message addr size() = %u", vAddr.size());
+ }
+
+ // Store the new addresses
+ vector<CAddress> vAddrOk;
+ int64_t nNow = GetAdjustedTime();
+ int64_t nSince = nNow - 10 * 60;
+ BOOST_FOREACH(CAddress& addr, vAddr)
+ {
+ boost::this_thread::interruption_point();
+
+ if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
+ continue;
+
+ if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
+ addr.nTime = nNow - 5 * 24 * 60 * 60;
+ pfrom->AddAddressKnown(addr);
+ bool fReachable = IsReachable(addr);
+ if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
+ {
+ // Relay to a limited number of other nodes
+ RelayAddress(addr, fReachable, connman);
+ }
+ // Do not store addresses outside our network
+ if (fReachable)
+ vAddrOk.push_back(addr);
+ }
+ connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ if (vAddr.size() < 1000)
+ pfrom->fGetAddr = false;
+ if (pfrom->fOneShot)
+ pfrom->fDisconnect = true;
+ }
+
+ else if (strCommand == NetMsgType::SENDHEADERS)
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fPreferHeaders = true;
+ }
+
+ else if (strCommand == NetMsgType::SENDCMPCT)
+ {
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 0;
+ vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
+ if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ LOCK(cs_main);
+ // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
+ if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
+ State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
+ }
+ if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
+ State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
+ if (pfrom->GetLocalServices() & NODE_WITNESS)
+ State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
+ else
+ State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::INV)
+ {
+ vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() > MAX_INV_SZ)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message inv size() = %u", vInv.size());
+ }
+
+ bool fBlocksOnly = !fRelayTxes;
+
+ // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
+ fBlocksOnly = false;
+
+ LOCK(cs_main);
+
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
+
+ std::vector<CInv> vToFetch;
+
+ for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
+ {
+ CInv &inv = vInv[nInv];
+
+ boost::this_thread::interruption_point();
+
+ bool fAlreadyHave = AlreadyHave(inv);
+ LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
+
+ if (inv.type == MSG_TX) {
+ inv.type |= nFetchFlags;
+ }
+
+ if (inv.type == MSG_BLOCK) {
+ UpdateBlockAvailability(pfrom->GetId(), inv.hash);
+ if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ // We used to request the full block here, but since headers-announcements are now the
+ // primary method of announcement on the network, and since, in the case that a node
+ // fell back to inv we probably have a reorg which we should get the headers for first,
+ // we now only provide a getheaders response here. When we receive the headers, we will
+ // then ask for the blocks we need.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
+ LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
+ }
+ }
+ else
+ {
+ pfrom->AddInventoryKnown(inv);
+ if (fBlocksOnly)
+ LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
+ else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload())
+ pfrom->AskFor(inv);
+ }
+
+ // Track requests for our stuff
+ GetMainSignals().Inventory(inv.hash);
+
+ if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) {
+ Misbehaving(pfrom->GetId(), 50);
+ return error("send buffer size() = %u", pfrom->nSendSize);
+ }
+ }
+
+ if (!vToFetch.empty())
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vToFetch));
+ }
+
+
+ else if (strCommand == NetMsgType::GETDATA)
+ {
+ vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() > MAX_INV_SZ)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message getdata size() = %u", vInv.size());
+ }
+
+ if (fDebug || (vInv.size() != 1))
+ LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
+
+ if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
+ LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
+
+ pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
+ }
+
+
+ else if (strCommand == NetMsgType::GETBLOCKS)
+ {
+ CBlockLocator locator;
+ uint256 hashStop;
+ vRecv >> locator >> hashStop;
+
+ LOCK(cs_main);
+
+ // Find the last block the caller has in the main chain
+ CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
+
+ // Send the rest of the chain
+ if (pindex)
+ pindex = chainActive.Next(pindex);
+ int nLimit = 500;
+ LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
+ for (; pindex; pindex = chainActive.Next(pindex))
+ {
+ if (pindex->GetBlockHash() == hashStop)
+ {
+ LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ break;
+ }
+ // If pruning, don't inv blocks unless we have on disk and are likely to still have
+ // for some reasonable time window (1 hour) that block relay might require.
+ const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ {
+ LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ break;
+ }
+ pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ if (--nLimit <= 0)
+ {
+ // When this block is requested, we'll send an inv that'll
+ // trigger the peer to getblocks the next batch of inventory.
+ LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ pfrom->hashContinue = pindex->GetBlockHash();
+ break;
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::GETBLOCKTXN)
+ {
+ BlockTransactionsRequest req;
+ vRecv >> req;
+
+ LOCK(cs_main);
+
+ BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
+ if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
+ LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
+ return true;
+ }
+
+ if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
+ // If an older block is requested (should never happen in practice,
+ // but can happen in tests) send a block response instead of a
+ // blocktxn response. Sending a full block response instead of a
+ // small blocktxn response is preferable in the case where a peer
+ // might maliciously send lots of getblocktxn requests to trigger
+ // expensive disk reads, because it will require the peer to
+ // actually receive all the data read from disk over the network.
+ LogPrint("net", "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH);
+ CInv inv;
+ inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ inv.hash = req.blockhash;
+ pfrom->vRecvGetData.push_back(inv);
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
+ return true;
+ }
+
+ CBlock block;
+ assert(ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()));
+
+ BlockTransactions resp(req);
+ for (size_t i = 0; i < req.indexes.size(); i++) {
+ if (req.indexes[i] >= block.vtx.size()) {
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id);
+ return true;
+ }
+ resp.txn[i] = block.vtx[req.indexes[i]];
+ }
+ int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ }
+
+
+ else if (strCommand == NetMsgType::GETHEADERS)
+ {
+ CBlockLocator locator;
+ uint256 hashStop;
+ vRecv >> locator >> hashStop;
+
+ LOCK(cs_main);
+ if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
+ return true;
+ }
+
+ CNodeState *nodestate = State(pfrom->GetId());
+ CBlockIndex* pindex = NULL;
+ if (locator.IsNull())
+ {
+ // If locator is null, return the hashStop block
+ BlockMap::iterator mi = mapBlockIndex.find(hashStop);
+ if (mi == mapBlockIndex.end())
+ return true;
+ pindex = (*mi).second;
+ }
+ else
+ {
+ // Find the last block the caller has in the main chain
+ pindex = FindForkInGlobalIndex(chainActive, locator);
+ if (pindex)
+ pindex = chainActive.Next(pindex);
+ }
+
+ // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
+ vector<CBlock> vHeaders;
+ int nLimit = MAX_HEADERS_RESULTS;
+ LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
+ for (; pindex; pindex = chainActive.Next(pindex))
+ {
+ vHeaders.push_back(pindex->GetBlockHeader());
+ if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
+ break;
+ }
+ // pindex can be NULL either if we sent chainActive.Tip() OR
+ // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // headers message). In both cases it's safe to update
+ // pindexBestHeaderSent to be our tip.
+ nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ }
+
+
+ else if (strCommand == NetMsgType::TX)
+ {
+ // Stop processing the transaction early if
+ // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
+ if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ {
+ LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
+ return true;
+ }
+
+ deque<COutPoint> vWorkQueue;
+ vector<uint256> vEraseQueue;
+ CTransaction tx(deserialize, vRecv);
+
+ CInv inv(MSG_TX, tx.GetHash());
+ pfrom->AddInventoryKnown(inv);
+
+ LOCK(cs_main);
+
+ bool fMissingInputs = false;
+ CValidationState state;
+
+ pfrom->setAskFor.erase(inv.hash);
+ mapAlreadyAskedFor.erase(inv.hash);
+
+ if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) {
+ mempool.check(pcoinsTip);
+ RelayTransaction(tx, connman);
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ vWorkQueue.emplace_back(inv.hash, i);
+ }
+
+ pfrom->nLastTXTime = GetTime();
+
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
+ pfrom->id,
+ tx.GetHash().ToString(),
+ mempool.size(), mempool.DynamicMemoryUsage() / 1000);
+
+ // Recursively process any orphan transactions that depended on this one
+ set<NodeId> setMisbehaving;
+ while (!vWorkQueue.empty()) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
+ vWorkQueue.pop_front();
+ if (itByPrev == mapOrphanTransactionsByPrev.end())
+ continue;
+ for (auto mi = itByPrev->second.begin();
+ mi != itByPrev->second.end();
+ ++mi)
+ {
+ const CTransaction& orphanTx = (*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ NodeId fromPeer = (*mi)->second.fromPeer;
+ bool fMissingInputs2 = false;
+ // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
+ // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
+ // anyone relaying LegitTxX banned)
+ CValidationState stateDummy;
+
+
+ if (setMisbehaving.count(fromPeer))
+ continue;
+ if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
+ LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
+ RelayTransaction(orphanTx, connman);
+ for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
+ vWorkQueue.emplace_back(orphanHash, i);
+ }
+ vEraseQueue.push_back(orphanHash);
+ }
+ else if (!fMissingInputs2)
+ {
+ int nDos = 0;
+ if (stateDummy.IsInvalid(nDos) && nDos > 0)
+ {
+ // Punish peer that gave us an invalid orphan tx
+ Misbehaving(fromPeer, nDos);
+ setMisbehaving.insert(fromPeer);
+ LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
+ }
+ // Has inputs but not accepted to mempool
+ // Probably non-standard or insufficient fee/priority
+ LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
+ vEraseQueue.push_back(orphanHash);
+ if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(orphanHash);
+ }
+ }
+ mempool.check(pcoinsTip);
+ }
+ }
+
+ BOOST_FOREACH(uint256 hash, vEraseQueue)
+ EraseOrphanTx(hash);
+ }
+ else if (fMissingInputs)
+ {
+ bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ if (recentRejects->contains(txin.prevout.hash)) {
+ fRejectedParents = true;
+ break;
+ }
+ }
+ if (!fRejectedParents) {
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
+ pfrom->AddInventoryKnown(_inv);
+ if (!AlreadyHave(_inv)) pfrom->AskFor(_inv);
+ }
+ AddOrphanTx(tx, pfrom->GetId());
+
+ // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
+ unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
+ unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
+ if (nEvicted > 0)
+ LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
+ } else {
+ LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
+ }
+ } else {
+ if (tx.wit.IsNull() && !state.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(tx.GetHash());
+ }
+
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ // Always relay transactions received from whitelisted peers, even
+ // if they were already in the mempool or rejected from it due
+ // to policy, allowing the node to function as a gateway for
+ // nodes hidden behind it.
+ //
+ // Never relay transactions that we would assign a non-zero DoS
+ // score for, as we expect peers to do the same with us in that
+ // case.
+ int nDoS = 0;
+ if (!state.IsInvalid(nDoS) || nDoS == 0) {
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
+ RelayTransaction(tx, connman);
+ } else {
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
+ }
+ }
+ }
+ int nDoS = 0;
+ if (state.IsInvalid(nDoS))
+ {
+ LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
+ pfrom->id,
+ FormatStateMessage(state));
+ if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
+ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
+ if (nDoS > 0) {
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ CBlockHeaderAndShortTxIDs cmpctblock;
+ vRecv >> cmpctblock;
+
+ {
+ LOCK(cs_main);
+
+ if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) {
+ // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
+ if (!IsInitialBlockDownload())
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ return true;
+ }
+ }
+
+ CBlockIndex *pindex = NULL;
+ CValidationState state;
+ if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
+ return true;
+ }
+ }
+
+ LOCK(cs_main);
+ // If AcceptBlockHeader returned true, it set pindex
+ assert(pindex);
+ UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+
+ std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
+ bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
+
+ if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
+ return true;
+
+ if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
+ pindex->nTx != 0) { // We had this block at some point, but pruned it
+ if (fAlreadyInFlight) {
+ // We requested this block for some reason, but our mempool will probably be useless
+ // so we just grab the block via normal getdata
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ }
+ return true;
+ }
+
+ // If we're not close to tip yet, give up and let parallel block fetch work its magic
+ if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
+ return true;
+
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
+ // Don't bother trying to process compact blocks from v1 peers
+ // after segwit activates.
+ return true;
+ }
+
+ // We want to be a bit conservative just to be extra careful about DoS
+ // possibilities in compact block processing...
+ if (pindex->nHeight <= chainActive.Height() + 2) {
+ if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
+ (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
+ list<QueuedBlock>::iterator *queuedBlockIt = NULL;
+ if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) {
+ if (!(*queuedBlockIt)->partialBlock)
+ (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
+ else {
+ // The block was already in flight using compact blocks from the same peer
+ LogPrint("net", "Peer sent us compact block we were already syncing!\n");
+ return true;
+ }
+ }
+
+ PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
+ ReadStatus status = partialBlock.InitData(cmpctblock);
+ if (status == READ_STATUS_INVALID) {
+ MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id);
+ return true;
+ } else if (status == READ_STATUS_FAILED) {
+ // Duplicate txindexes, the block is now in-flight, so just request it
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return true;
+ }
+
+ if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ // We seem to be rather well-synced, so it appears pfrom was the first to provide us
+ // with this block! Let's get them to announce using compact blocks in the future.
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
+ }
+
+ BlockTransactionsRequest req;
+ for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
+ if (!partialBlock.IsTxAvailable(i))
+ req.indexes.push_back(i);
+ }
+ if (req.indexes.empty()) {
+ // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
+ BlockTransactions txn;
+ txn.blockhash = cmpctblock.header.GetHash();
+ CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
+ blockTxnMsg << txn;
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman);
+ } else {
+ req.blockhash = pindex->GetBlockHash();
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ }
+ }
+ } else {
+ if (fAlreadyInFlight) {
+ // We requested this block, but its far into the future, so our
+ // mempool will probably be useless - request the block normally
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return true;
+ } else {
+ // If this was an announce-cmpctblock, we want the same treatment as a header message
+ // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
+ std::vector<CBlock> headers;
+ headers.push_back(cmpctblock.header);
+ CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
+ vHeadersMsg << headers;
+ return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
+ }
+ }
+ }
+
+ else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ BlockTransactions resp;
+ vRecv >> resp;
+
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ bool fBlockRead = false;
+ {
+ LOCK(cs_main);
+
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
+ if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
+ it->second.first != pfrom->GetId()) {
+ LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
+ return true;
+ }
+
+ PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
+ ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
+ if (status == READ_STATUS_INVALID) {
+ MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
+ return true;
+ } else if (status == READ_STATUS_FAILED) {
+ // Might have collided, fall back to getdata now :(
+ std::vector<CInv> invs;
+ invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ } else {
+ // Block is either okay, or possibly we received
+ // READ_STATUS_CHECKBLOCK_FAILED.
+ // Note that CheckBlock can only fail for one of a few reasons:
+ // 1. bad-proof-of-work (impossible here, because we've already
+ // accepted the header)
+ // 2. merkleroot doesn't match the transactions given (already
+ // caught in FillBlock with READ_STATUS_FAILED, so
+ // impossible here)
+ // 3. the block is otherwise invalid (eg invalid coinbase,
+ // block is too big, too many legacy sigops, etc).
+ // So if CheckBlock failed, #3 is the only possibility.
+ // Under BIP 152, we don't DoS-ban unless proof of work is
+ // invalid (we don't require all the stateless checks to have
+ // been run). This is handled below, so just treat this as
+ // though the block was successfully read, and rely on the
+ // handling in ProcessNewBlock to ensure the block index is
+ // updated, reject messages go out, etc.
+ MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ fBlockRead = true;
+ // mapBlockSource is only used for sending reject messages and DoS scores,
+ // so the race between here and cs_main in ProcessNewBlock is fine.
+ // BIP 152 permits peers to relay compact blocks after validating
+ // the header only; we should not punish peers if the block turns
+ // out to be invalid.
+ mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
+ }
+ } // Don't hold cs_main when we call into ProcessNewBlock
+ if (fBlockRead) {
+ bool fNewBlock = false;
+ // Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
+ // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
+ ProcessNewBlock(chainparams, pblock, true, NULL, &fNewBlock);
+ if (fNewBlock)
+ pfrom->nLastBlockTime = GetTime();
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
+ {
+ std::vector<CBlockHeader> headers;
+
+ // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
+ unsigned int nCount = ReadCompactSize(vRecv);
+ if (nCount > MAX_HEADERS_RESULTS) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("headers message size = %u", nCount);
+ }
+ headers.resize(nCount);
+ for (unsigned int n = 0; n < nCount; n++) {
+ vRecv >> headers[n];
+ ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
+ }
+
+ if (nCount == 0) {
+ // Nothing interesting. Stop asking this peers for more headers.
+ return true;
+ }
+
+ CBlockIndex *pindexLast = NULL;
+ {
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ // If this looks like it could be a block announcement (nCount <
+ // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
+ // don't connect:
+ // - Send a getheaders message in response to try to connect the chain.
+ // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
+ // don't connect before giving DoS points
+ // - Once a headers message is received that is valid and does connect,
+ // nUnconnectingHeaders gets reset back to 0.
+ if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
+ nodestate->nUnconnectingHeaders++;
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
+ headers[0].GetHash().ToString(),
+ headers[0].hashPrevBlock.ToString(),
+ pindexBestHeader->nHeight,
+ pfrom->id, nodestate->nUnconnectingHeaders);
+ // Set hashLastUnknownBlock for this peer, so that if we
+ // eventually get the headers - even from a different peer -
+ // we can use this peer to download.
+ UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
+
+ if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
+ Misbehaving(pfrom->GetId(), 20);
+ }
+ return true;
+ }
+
+ uint256 hashLastBlock;
+ for (const CBlockHeader& header : headers) {
+ if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
+ Misbehaving(pfrom->GetId(), 20);
+ return error("non-continuous headers sequence");
+ }
+ hashLastBlock = header.GetHash();
+ }
+ }
+
+ CValidationState state;
+ if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ return error("invalid header received");
+ }
+ }
+
+ {
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom->GetId());
+ if (nodestate->nUnconnectingHeaders > 0) {
+ LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
+ }
+ nodestate->nUnconnectingHeaders = 0;
+
+ assert(pindexLast);
+ UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+
+ if (nCount == MAX_HEADERS_RESULTS) {
+ // Headers message had its maximum size; the peer may have more headers.
+ // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
+ // from there instead.
+ LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
+ }
+
+ bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ // If this set of headers is valid and ends in a block with at least as
+ // much work as our tip, download as much as possible.
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
+ vector<CBlockIndex *> vToFetch;
+ CBlockIndex *pindexWalk = pindexLast;
+ // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
+ !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
+ (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
+ // We don't have this block, and it's not yet in flight.
+ vToFetch.push_back(pindexWalk);
+ }
+ pindexWalk = pindexWalk->pprev;
+ }
+ // If pindexWalk still isn't on our main chain, we're looking at a
+ // very large reorg at a time we think we're close to caught up to
+ // the main chain -- this shouldn't really happen. Bail out on the
+ // direct fetch and rely on parallel download instead.
+ if (!chainActive.Contains(pindexWalk)) {
+ LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus());
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
+ LogPrint("net", "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom->id);
+ }
+ if (vGetData.size() > 1) {
+ LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
+ pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ }
+ if (vGetData.size() > 0) {
+ if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ // We seem to be rather well-synced, so it appears pfrom was the first to provide us
+ // with this block! Let's get them to announce using compact blocks in the future.
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
+ // In any case, we want to download using a compact block, not a regular one
+ vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
+ }
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ }
+ }
+ }
+ }
+ }
+
+ else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ vRecv >> *pblock;
+
+ LogPrint("net", "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->id);
+
+ // Process all blocks from whitelisted peers, even if not requested,
+ // unless we're still syncing with the network.
+ // Such an unrequested block may still be processed, subject to the
+ // conditions in AcceptBlock().
+ bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
+ const uint256 hash(pblock->GetHash());
+ {
+ LOCK(cs_main);
+ // Also always process if we requested the block explicitly, as we may
+ // need it even though it is not a candidate for a new best tip.
+ forceProcessing |= MarkBlockAsReceived(hash);
+ // mapBlockSource is only used for sending reject messages and DoS scores,
+ // so the race between here and cs_main in ProcessNewBlock is fine.
+ mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
+ }
+ bool fNewBlock = false;
+ ProcessNewBlock(chainparams, pblock, forceProcessing, NULL, &fNewBlock);
+ if (fNewBlock)
+ pfrom->nLastBlockTime = GetTime();
+ }
+
+
+ else if (strCommand == NetMsgType::GETADDR)
+ {
+ // This asymmetric behavior for inbound and outbound connections was introduced
+ // to prevent a fingerprinting attack: an attacker can send specific fake addresses
+ // to users' AddrMan and later request them by sending getaddr messages.
+ // Making nodes which are behind NAT and can only make outgoing connections ignore
+ // the getaddr message mitigates the attack.
+ if (!pfrom->fInbound) {
+ LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id);
+ return true;
+ }
+
+ // Only send one GetAddr response per connection to reduce resource waste
+ // and discourage addr stamping of INV announcements.
+ if (pfrom->fSentAddr) {
+ LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
+ return true;
+ }
+ pfrom->fSentAddr = true;
+
+ pfrom->vAddrToSend.clear();
+ vector<CAddress> vAddr = connman.GetAddresses();
+ FastRandomContext insecure_rand;
+ BOOST_FOREACH(const CAddress &addr, vAddr)
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+
+
+ else if (strCommand == NetMsgType::MEMPOOL)
+ {
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ LOCK(pfrom->cs_inventory);
+ pfrom->fSendMempool = true;
+ }
+
+
+ else if (strCommand == NetMsgType::PING)
+ {
+ if (pfrom->nVersion > BIP0031_VERSION)
+ {
+ uint64_t nonce = 0;
+ vRecv >> nonce;
+ // Echo the message back with the nonce. This allows for two useful features:
+ //
+ // 1) A remote node can quickly check if the connection is operational
+ // 2) Remote nodes can measure the latency of the network thread. If this node
+ // is overloaded it won't respond to pings quickly and the remote node can
+ // avoid sending us more work, like chain download requests.
+ //
+ // The nonce stops the remote getting confused between different pings: without
+ // it, if the remote node sends a ping once per second and this node takes 5
+ // seconds to respond to each, the 5th ping the remote sends would appear to
+ // return very quickly.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::PONG)
+ {
+ int64_t pingUsecEnd = nTimeReceived;
+ uint64_t nonce = 0;
+ size_t nAvail = vRecv.in_avail();
+ bool bPingFinished = false;
+ std::string sProblem;
+
+ if (nAvail >= sizeof(nonce)) {
+ vRecv >> nonce;
+
+ // Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
+ if (pfrom->nPingNonceSent != 0) {
+ if (nonce == pfrom->nPingNonceSent) {
+ // Matching pong received, this ping is no longer outstanding
+ bPingFinished = true;
+ int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
+ if (pingUsecTime > 0) {
+ // Successful ping time measurement, replace previous
+ pfrom->nPingUsecTime = pingUsecTime;
+ pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
+ } else {
+ // This should never happen
+ sProblem = "Timing mishap";
+ }
+ } else {
+ // Nonce mismatches are normal when pings are overlapping
+ sProblem = "Nonce mismatch";
+ if (nonce == 0) {
+ // This is most likely a bug in another implementation somewhere; cancel this ping
+ bPingFinished = true;
+ sProblem = "Nonce zero";
+ }
+ }
+ } else {
+ sProblem = "Unsolicited pong without ping";
+ }
+ } else {
+ // This is most likely a bug in another implementation somewhere; cancel this ping
+ bPingFinished = true;
+ sProblem = "Short payload";
+ }
+
+ if (!(sProblem.empty())) {
+ LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
+ pfrom->id,
+ sProblem,
+ pfrom->nPingNonceSent,
+ nonce,
+ nAvail);
+ }
+ if (bPingFinished) {
+ pfrom->nPingNonceSent = 0;
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERLOAD)
+ {
+ CBloomFilter filter;
+ vRecv >> filter;
+
+ if (!filter.IsWithinSizeConstraints())
+ {
+ // There is no excuse for sending a too-large filter
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ }
+ else
+ {
+ LOCK(pfrom->cs_filter);
+ delete pfrom->pfilter;
+ pfrom->pfilter = new CBloomFilter(filter);
+ pfrom->pfilter->UpdateEmptyFull();
+ pfrom->fRelayTxes = true;
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERADD)
+ {
+ vector<unsigned char> vData;
+ vRecv >> vData;
+
+ // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
+ // and thus, the maximum size any matched object can have) in a filteradd message
+ bool bad = false;
+ if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ bad = true;
+ } else {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter) {
+ pfrom->pfilter->insert(vData);
+ } else {
+ bad = true;
+ }
+ }
+ if (bad) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERCLEAR)
+ {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->GetLocalServices() & NODE_BLOOM) {
+ delete pfrom->pfilter;
+ pfrom->pfilter = new CBloomFilter();
+ }
+ pfrom->fRelayTxes = true;
+ }
+
+
+ else if (strCommand == NetMsgType::REJECT)
+ {
+ if (fDebug) {
+ try {
+ string strMsg; unsigned char ccode; string strReason;
+ vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
+
+ ostringstream ss;
+ ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
+
+ if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
+ {
+ uint256 hash;
+ vRecv >> hash;
+ ss << ": hash " << hash.ToString();
+ }
+ LogPrint("net", "Reject %s\n", SanitizeString(ss.str()));
+ } catch (const std::ios_base::failure&) {
+ // Avoid feedback loops by preventing reject messages from triggering a new reject message.
+ LogPrint("net", "Unparseable reject message received\n");
+ }
+ }
+ }
+
+ else if (strCommand == NetMsgType::FEEFILTER) {
+ CAmount newFeeFilter = 0;
+ vRecv >> newFeeFilter;
+ if (MoneyRange(newFeeFilter)) {
+ {
+ LOCK(pfrom->cs_feeFilter);
+ pfrom->minFeeFilter = newFeeFilter;
+ }
+ LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
+ }
+ }
+
+ else if (strCommand == NetMsgType::NOTFOUND) {
+ // We do not care about the NOTFOUND message, but logging an Unknown Command
+ // message would be undesirable as we transmit it ourselves.
+ }
+
+ else {
+ // Ignore unknown commands for extensibility
+ LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
+ }
+
+
+
+ return true;
+}
+
+// requires LOCK(cs_vRecvMsg)
+bool ProcessMessages(CNode* pfrom, CConnman& connman)
+{
+ const CChainParams& chainparams = Params();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+ //if (fDebug)
+ // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size());
+
+ //
+ // Message format
+ // (4) message start
+ // (12) command
+ // (4) size
+ // (4) checksum
+ // (x) data
+ //
+ bool fOk = true;
+
+ if (!pfrom->vRecvGetData.empty())
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
+
+ // this maintains the order of responses
+ if (!pfrom->vRecvGetData.empty()) return fOk;
+
+ std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin();
+ while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) {
+ // Don't bother if send buffer is too full to respond anyway
+ if (pfrom->nSendSize >= nMaxSendBufferSize)
+ break;
+
+ // get next message
+ CNetMessage& msg = *it;
+
+ //if (fDebug)
+ // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__,
+ // msg.hdr.nMessageSize, msg.vRecv.size(),
+ // msg.complete() ? "Y" : "N");
+
+ // end, if an incomplete message is found
+ if (!msg.complete())
+ break;
+
+ // at this point, any failure means we can delete the current message
+ it++;
+
+ // Scan for message start
+ if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
+ LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
+ fOk = false;
+ break;
+ }
+
+ // Read header
+ CMessageHeader& hdr = msg.hdr;
+ if (!hdr.IsValid(chainparams.MessageStart()))
+ {
+ LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
+ continue;
+ }
+ string strCommand = hdr.GetCommand();
+
+ // Message size
+ unsigned int nMessageSize = hdr.nMessageSize;
+
+ // Checksum
+ CDataStream& vRecv = msg.vRecv;
+ const uint256& hash = msg.GetMessageHash();
+ if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ {
+ LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
+ SanitizeString(strCommand), nMessageSize,
+ HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
+ HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
+ continue;
+ }
+
+ // Process message
+ bool fRet = false;
+ try
+ {
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman);
+ boost::this_thread::interruption_point();
+ }
+ catch (const std::ios_base::failure& e)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")));
+ if (strstr(e.what(), "end of data"))
+ {
+ // Allow exceptions from under-length message on vRecv
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else if (strstr(e.what(), "size too large"))
+ {
+ // Allow exceptions from over-long size
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
+ {
+ // Allow exceptions from non-canonical encoding
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else
+ {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ }
+ }
+ catch (const boost::thread_interrupted&) {
+ throw;
+ }
+ catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "ProcessMessages()");
+ }
+
+ if (!fRet)
+ LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
+
+ break;
+ }
+
+ // In case the connection got shut down, its receive buffer was wiped
+ if (!pfrom->fDisconnect)
+ pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
+
+ return fOk;
+}
+
+class CompareInvMempoolOrder
+{
+ CTxMemPool *mp;
+public:
+ CompareInvMempoolOrder(CTxMemPool *_mempool)
+ {
+ mp = _mempool;
+ }
+
+ bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
+ {
+ /* As std::make_heap produces a max-heap, we want the entries with the
+ * fewest ancestors/highest fee to sort later. */
+ return mp->CompareDepthAndScore(*b, *a);
+ }
+};
+
+bool SendMessages(CNode* pto, CConnman& connman)
+{
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ {
+ // Don't send anything until we get its version message
+ if (pto->nVersion == 0 || pto->fDisconnect)
+ return true;
+
+ // If we get here, the outgoing message serialization version is set and can't change.
+ CNetMsgMaker msgMaker(pto->GetSendVersion());
+
+ //
+ // Message: ping
+ //
+ bool pingSend = false;
+ if (pto->fPingQueued) {
+ // RPC ping request by user
+ pingSend = true;
+ }
+ if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
+ // Ping automatically sent as a latency probe & keepalive.
+ pingSend = true;
+ }
+ if (pingSend) {
+ uint64_t nonce = 0;
+ while (nonce == 0) {
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
+ }
+ pto->fPingQueued = false;
+ pto->nPingUsecStart = GetTimeMicros();
+ if (pto->nVersion > BIP0031_VERSION) {
+ pto->nPingNonceSent = nonce;
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
+ } else {
+ // Peer is too old to support ping command with nonce, pong will never arrive.
+ pto->nPingNonceSent = 0;
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
+ }
+ }
+
+ TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
+ if (!lockMain)
+ return true;
+
+ CNodeState &state = *State(pto->GetId());
+
+ BOOST_FOREACH(const CBlockReject& reject, state.rejects)
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
+ state.rejects.clear();
+
+ if (state.fShouldBan) {
+ state.fShouldBan = false;
+ if (pto->fWhitelisted)
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
+ else {
+ pto->fDisconnect = true;
+ if (pto->addr.IsLocal())
+ LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
+ else
+ {
+ connman.Ban(pto->addr, BanReasonNodeMisbehaving);
+ }
+ return true;
+ }
+ }
+
+ // Address refresh broadcast
+ int64_t nNow = GetTimeMicros();
+ if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ AdvertiseLocal(pto);
+ pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
+ }
+
+ //
+ // Message: addr
+ //
+ if (pto->nNextAddrSend < nNow) {
+ pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
+ vector<CAddress> vAddr;
+ vAddr.reserve(pto->vAddrToSend.size());
+ BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
+ {
+ if (!pto->addrKnown.contains(addr.GetKey()))
+ {
+ pto->addrKnown.insert(addr.GetKey());
+ vAddr.push_back(addr);
+ // receiver rejects addr messages larger than 1000
+ if (vAddr.size() >= 1000)
+ {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ vAddr.clear();
+ }
+ }
+ }
+ pto->vAddrToSend.clear();
+ if (!vAddr.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ // we only send the big addr message once
+ if (pto->vAddrToSend.capacity() > 40)
+ pto->vAddrToSend.shrink_to_fit();
+ }
+
+ // Start block sync
+ if (pindexBestHeader == NULL)
+ pindexBestHeader = chainActive.Tip();
+ bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
+ if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
+ // Only actively request headers from a single peer, unless we're close to today.
+ if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ state.fSyncStarted = true;
+ nSyncStarted++;
+ const CBlockIndex *pindexStart = pindexBestHeader;
+ /* If possible, start at the block preceding the currently
+ best known header. This ensures that we always get a
+ non-empty list of headers back as long as the peer
+ is up-to-date. With a non-empty response, we can initialise
+ the peer's known best block. This wouldn't be possible
+ if we requested starting at pindexBestHeader and
+ got back an empty response. */
+ if (pindexStart->pprev)
+ pindexStart = pindexStart->pprev;
+ LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
+ }
+ }
+
+ // Resend wallet transactions that haven't gotten in a block yet
+ // Except during reindex, importing and IBD, when old wallet
+ // transactions become unconfirmed and spams other nodes.
+ if (!fReindex && !fImporting && !IsInitialBlockDownload())
+ {
+ GetMainSignals().Broadcast(nTimeBestReceived, &connman);
+ }
+
+ //
+ // Try sending block announcements via headers
+ //
+ {
+ // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
+ // list of block hashes we're relaying, and our peer wants
+ // headers announcements, then find the first header
+ // not yet known to our peer but would connect, and send.
+ // If no header would connect, or if we have too many
+ // blocks, or if the peer doesn't want headers, just
+ // add all to the inv queue.
+ LOCK(pto->cs_inventory);
+ vector<CBlock> vHeaders;
+ bool fRevertToInv = ((!state.fPreferHeaders &&
+ (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) ||
+ pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
+ CBlockIndex *pBestIndex = NULL; // last header queued for delivery
+ ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
+
+ if (!fRevertToInv) {
+ bool fFoundStartingHeader = false;
+ // Try to find first header that our peer doesn't have, and
+ // then send all headers past that one. If we come across any
+ // headers that aren't on chainActive, give up.
+ BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
+ BlockMap::iterator mi = mapBlockIndex.find(hash);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // Bail out if we reorged away from this block
+ fRevertToInv = true;
+ break;
+ }
+ if (pBestIndex != NULL && pindex->pprev != pBestIndex) {
+ // This means that the list of blocks to announce don't
+ // connect to each other.
+ // This shouldn't really be possible to hit during
+ // regular operation (because reorgs should take us to
+ // a chain that has some block not on the prior chain,
+ // which should be caught by the prior check), but one
+ // way this could happen is by using invalidateblock /
+ // reconsiderblock repeatedly on the tip, causing it to
+ // be added multiple times to vBlockHashesToAnnounce.
+ // Robustly deal with this rare situation by reverting
+ // to an inv.
+ fRevertToInv = true;
+ break;
+ }
+ pBestIndex = pindex;
+ if (fFoundStartingHeader) {
+ // add this to the headers message
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else if (PeerHasHeader(&state, pindex)) {
+ continue; // keep looking for the first new block
+ } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
+ // Peer doesn't have this header but they do have the prior one.
+ // Start sending headers.
+ fFoundStartingHeader = true;
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else {
+ // Peer doesn't have this header or the prior one -- nothing will
+ // connect, so bail out.
+ fRevertToInv = true;
+ break;
+ }
+ }
+ }
+ if (!fRevertToInv && !vHeaders.empty()) {
+ if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
+ // We only send up to 1 block as header-and-ids, as otherwise
+ // probably means we're doing an initial-ish-sync or they're slow
+ LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ //TODO: Shouldn't need to reload block from disk, but requires refactor
+ CBlock block;
+ assert(ReadBlockFromDisk(block, pBestIndex, consensusParams));
+ CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
+ int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ state.pindexBestHeaderSent = pBestIndex;
+ } else if (state.fPreferHeaders) {
+ if (vHeaders.size() > 1) {
+ LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
+ vHeaders.size(),
+ vHeaders.front().GetHash().ToString(),
+ vHeaders.back().GetHash().ToString(), pto->id);
+ } else {
+ LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ }
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ state.pindexBestHeaderSent = pBestIndex;
+ } else
+ fRevertToInv = true;
+ }
+ if (fRevertToInv) {
+ // If falling back to using an inv, just try to inv the tip.
+ // The last entry in vBlockHashesToAnnounce was our tip at some point
+ // in the past.
+ if (!pto->vBlockHashesToAnnounce.empty()) {
+ const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
+ BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+
+ // Warn if we're announcing a block that is not on the main chain.
+ // This should be very rare and could be optimized out.
+ // Just log for now.
+ if (chainActive[pindex->nHeight] != pindex) {
+ LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
+ hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ }
+
+ // If the peer's chain has this block, don't inv it back.
+ if (!PeerHasHeader(&state, pindex)) {
+ pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
+ pto->id, hashToAnnounce.ToString());
+ }
+ }
+ }
+ pto->vBlockHashesToAnnounce.clear();
+ }
+
+ //
+ // Message: inventory
+ //
+ vector<CInv> vInv;
+ {
+ LOCK(pto->cs_inventory);
+ vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX));
+
+ // Add blocks
+ BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) {
+ vInv.push_back(CInv(MSG_BLOCK, hash));
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ }
+ pto->vInventoryBlockToSend.clear();
+
+ // Check whether periodic sends should happen
+ bool fSendTrickle = pto->fWhitelisted;
+ if (pto->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
+ pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
+ }
+
+ // Time to send but the peer has requested we not relay transactions.
+ if (fSendTrickle) {
+ LOCK(pto->cs_filter);
+ if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
+ }
+
+ // Respond to BIP35 mempool requests
+ if (fSendTrickle && pto->fSendMempool) {
+ auto vtxinfo = mempool.infoAll();
+ pto->fSendMempool = false;
+ CAmount filterrate = 0;
+ {
+ LOCK(pto->cs_feeFilter);
+ filterrate = pto->minFeeFilter;
+ }
+
+ LOCK(pto->cs_filter);
+
+ for (const auto& txinfo : vtxinfo) {
+ const uint256& hash = txinfo.tx->GetHash();
+ CInv inv(MSG_TX, hash);
+ pto->setInventoryTxToSend.erase(hash);
+ if (filterrate) {
+ if (txinfo.feeRate.GetFeePerK() < filterrate)
+ continue;
+ }
+ if (pto->pfilter) {
+ if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ }
+ pto->filterInventoryKnown.insert(hash);
+ vInv.push_back(inv);
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ }
+ pto->timeLastMempoolReq = GetTime();
+ }
+
+ // Determine transactions to relay
+ if (fSendTrickle) {
+ // Produce a vector with all candidates for sending
+ vector<std::set<uint256>::iterator> vInvTx;
+ vInvTx.reserve(pto->setInventoryTxToSend.size());
+ for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
+ vInvTx.push_back(it);
+ }
+ CAmount filterrate = 0;
+ {
+ LOCK(pto->cs_feeFilter);
+ filterrate = pto->minFeeFilter;
+ }
+ // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
+ // A heap is used so that not all items need sorting if only a few are being sent.
+ CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
+ std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ // No reason to drain out at many times the network's capacity,
+ // especially since we have many peers and some will draw much shorter delays.
+ unsigned int nRelayedTransactions = 0;
+ LOCK(pto->cs_filter);
+ while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
+ // Fetch the top element from the heap
+ std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ std::set<uint256>::iterator it = vInvTx.back();
+ vInvTx.pop_back();
+ uint256 hash = *it;
+ // Remove it from the to-be-sent set
+ pto->setInventoryTxToSend.erase(it);
+ // Check if not in the filter already
+ if (pto->filterInventoryKnown.contains(hash)) {
+ continue;
+ }
+ // Not in the mempool anymore? don't bother sending it.
+ auto txinfo = mempool.info(hash);
+ if (!txinfo.tx) {
+ continue;
+ }
+ if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
+ continue;
+ }
+ if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ // Send
+ vInv.push_back(CInv(MSG_TX, hash));
+ nRelayedTransactions++;
+ {
+ // Expire old relay messages
+ while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
+ {
+ mapRelay.erase(vRelayExpiration.front().second);
+ vRelayExpiration.pop_front();
+ }
+
+ auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
+ if (ret.second) {
+ vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
+ }
+ }
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ pto->filterInventoryKnown.insert(hash);
+ }
+ }
+ }
+ if (!vInv.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+
+ // Detect whether we're stalling
+ nNow = GetTimeMicros();
+ if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
+ // Stalling only triggers when the block download window cannot move. During normal steady state,
+ // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
+ // should only happen during initial block download.
+ LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
+ pto->fDisconnect = true;
+ return true;
+ }
+ // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
+ // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
+ // We compensate for other peers to prevent killing off peers due to our own downstream link
+ // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
+ // to unreasonably increase our timeout.
+ if (state.vBlocksInFlight.size() > 0) {
+ QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
+ int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
+ pto->fDisconnect = true;
+ return true;
+ }
+ }
+
+ //
+ // Message: getdata (blocks)
+ //
+ vector<CInv> vGetData;
+ if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ vector<CBlockIndex*> vToDownload;
+ NodeId staller = -1;
+ FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
+ BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
+ uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
+ LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+ pindex->nHeight, pto->id);
+ }
+ if (state.nBlocksInFlight == 0 && staller != -1) {
+ if (State(staller)->nStallingSince == 0) {
+ State(staller)->nStallingSince = nNow;
+ LogPrint("net", "Stall started peer=%d\n", staller);
+ }
+ }
+ }
+
+ //
+ // Message: getdata (non-blocks)
+ //
+ while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
+ {
+ const CInv& inv = (*pto->mapAskFor.begin()).second;
+ if (!AlreadyHave(inv))
+ {
+ if (fDebug)
+ LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
+ vGetData.push_back(inv);
+ if (vGetData.size() >= 1000)
+ {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ vGetData.clear();
+ }
+ } else {
+ //If we're not going to ask, don't expect a response.
+ pto->setAskFor.erase(inv.hash);
+ }
+ pto->mapAskFor.erase(pto->mapAskFor.begin());
+ }
+ if (!vGetData.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+
+ //
+ // Message: feefilter
+ //
+ // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
+ if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
+ !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
+ CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ int64_t timeNow = GetTimeMicros();
+ if (timeNow > pto->nextSendTimeFeeFilter) {
+ static FeeFilterRounder filterRounder(::minRelayTxFee);
+ CAmount filterToSend = filterRounder.round(currentFilter);
+ if (filterToSend != pto->lastSentFeeFilter) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
+ pto->lastSentFeeFilter = filterToSend;
+ }
+ pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ }
+ // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
+ // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
+ else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
+ (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
+ pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ }
+ }
+ }
+ return true;
+}
+
+class CNetProcessingCleanup
+{
+public:
+ CNetProcessingCleanup() {}
+ ~CNetProcessingCleanup() {
+ // orphan transactions
+ mapOrphanTransactions.clear();
+ mapOrphanTransactionsByPrev.clear();
+ }
+} instance_of_cnetprocessingcleanup;
diff --git a/src/net_processing.h b/src/net_processing.h
new file mode 100644
index 0000000000..130433cc7c
--- /dev/null
+++ b/src/net_processing.h
@@ -0,0 +1,51 @@
+// 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_NET_PROCESSING_H
+#define BITCOIN_NET_PROCESSING_H
+
+#include "net.h"
+#include "validationinterface.h"
+
+/** Register with a network node to receive its signals */
+void RegisterNodeSignals(CNodeSignals& nodeSignals);
+/** Unregister a network node */
+void UnregisterNodeSignals(CNodeSignals& nodeSignals);
+
+class PeerLogicValidation : public CValidationInterface {
+private:
+ CConnman* connman;
+
+public:
+ PeerLogicValidation(CConnman* connmanIn);
+
+ virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
+ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
+ virtual void BlockChecked(const CBlock& block, const CValidationState& state);
+};
+
+struct CNodeStateStats {
+ int nMisbehavior;
+ int nSyncHeight;
+ int nCommonHeight;
+ std::vector<int> vHeightInFlight;
+};
+
+/** Get statistics from node state */
+bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
+/** Increase a node's misbehavior score. */
+void Misbehaving(NodeId nodeid, int howmuch);
+
+/** Process protocol messages received from a given node */
+bool ProcessMessages(CNode* pfrom, CConnman& connman);
+/**
+ * Send queued protocol messages to be sent to a give node.
+ *
+ * @param[in] pto The node which we are sending messages to.
+ * @param[in] connman The connection manager for that node.
+ */
+bool SendMessages(CNode* pto, CConnman& connman);
+
+#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 9fe34108f5..9118584b80 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -94,30 +94,9 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
}
}
-#ifdef HAVE_GETADDRINFO_A
- struct in_addr ipv4_addr;
-#ifdef HAVE_INET_PTON
- if (inet_pton(AF_INET, pszName, &ipv4_addr) > 0) {
- vIP.push_back(CNetAddr(ipv4_addr));
- return true;
- }
-
- struct in6_addr ipv6_addr;
- if (inet_pton(AF_INET6, pszName, &ipv6_addr) > 0) {
- vIP.push_back(CNetAddr(ipv6_addr));
- return true;
- }
-#else
- ipv4_addr.s_addr = inet_addr(pszName);
- if (ipv4_addr.s_addr != INADDR_NONE) {
- vIP.push_back(CNetAddr(ipv4_addr));
- return true;
- }
-#endif
-#endif
-
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
+
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
aiHint.ai_family = AF_UNSPEC;
@@ -126,33 +105,8 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
#else
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
-
struct addrinfo *aiRes = NULL;
-#ifdef HAVE_GETADDRINFO_A
- struct gaicb gcb, *query = &gcb;
- memset(query, 0, sizeof(struct gaicb));
- gcb.ar_name = pszName;
- gcb.ar_request = &aiHint;
- int nErr = getaddrinfo_a(GAI_NOWAIT, &query, 1, NULL);
- if (nErr)
- return false;
-
- do {
- // Should set the timeout limit to a reasonable value to avoid
- // generating unnecessary checking call during the polling loop,
- // while it can still response to stop request quick enough.
- // 2 seconds looks fine in our situation.
- struct timespec ts = { 2, 0 };
- gai_suspend(&query, 1, &ts);
- boost::this_thread::interruption_point();
-
- nErr = gai_error(query);
- if (0 == nErr)
- aiRes = query->ar_result;
- } while (nErr == EAI_INPROGRESS);
-#else
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
-#endif
if (nErr)
return false;
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 7113390cdf..9eb831bc17 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -404,7 +404,8 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
{
// Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget <= 1 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
return CFeeRate(0);
double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
@@ -423,6 +424,10 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
return CFeeRate(0);
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget == 1)
+ confTarget = 2;
+
double median = -1;
while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 3ad1ff7bae..0c71a079fb 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -7,7 +7,7 @@
#include "policy/policy.h"
-#include "main.h"
+#include "validation.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 91f4d29488..11d7eace55 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -62,9 +62,9 @@ uint256 CMutableTransaction::GetHash() const
return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
}
-void CTransaction::UpdateHash() const
+uint256 CTransaction::ComputeHash() const
{
- *const_cast<uint256*>(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
+ return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
}
uint256 CTransaction::GetWitnessHash() const
@@ -72,25 +72,10 @@ uint256 CTransaction::GetWitnessHash() const
return SerializeHash(*this, SER_GETHASH, 0);
}
-CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
-
-CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {
- UpdateHash();
-}
-
-CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime) {
- UpdateHash();
-}
-
-CTransaction& CTransaction::operator=(const CTransaction &tx) {
- *const_cast<int*>(&nVersion) = tx.nVersion;
- *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
- *const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
- *const_cast<CTxWitness*>(&wit) = tx.wit;
- *const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
- *const_cast<uint256*>(&hash) = tx.hash;
- return *this;
-}
+/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */
+CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {}
+CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
+CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
CAmount CTransaction::GetValueOut() const
{
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 0fa85a1519..66fefafef5 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -286,73 +286,81 @@ struct CMutableTransaction;
* - CTxWitness wit;
* - uint32_t nLockTime
*/
-template<typename Stream, typename Operation, typename TxType>
-inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action) {
+template<typename Stream, typename TxType>
+inline void UnserializeTransaction(TxType& tx, Stream& s) {
const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
- READWRITE(*const_cast<int32_t*>(&tx.nVersion));
+ s >> tx.nVersion;
unsigned char flags = 0;
- if (ser_action.ForRead()) {
- const_cast<std::vector<CTxIn>*>(&tx.vin)->clear();
- const_cast<std::vector<CTxOut>*>(&tx.vout)->clear();
- const_cast<CTxWitness*>(&tx.wit)->SetNull();
- /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- if (tx.vin.size() == 0 && fAllowWitness) {
- /* We read a dummy or an empty vin. */
- READWRITE(flags);
- if (flags != 0) {
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- }
- } else {
- /* We read a non-empty vin. Assume a normal vout follows. */
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- }
- if ((flags & 1) && fAllowWitness) {
- /* The witness flag is present, and we support witnesses. */
- flags ^= 1;
- const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
- READWRITE(tx.wit);
- }
- if (flags) {
- /* Unknown flag in the serialization */
- throw std::ios_base::failure("Unknown transaction optional data");
+ tx.vin.clear();
+ tx.vout.clear();
+ tx.wit.SetNull();
+ /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
+ s >> tx.vin;
+ if (tx.vin.size() == 0 && fAllowWitness) {
+ /* We read a dummy or an empty vin. */
+ s >> flags;
+ if (flags != 0) {
+ s >> tx.vin;
+ s >> tx.vout;
}
} else {
- // Consistency check
- assert(tx.wit.vtxinwit.size() <= tx.vin.size());
- if (fAllowWitness) {
- /* Check whether witnesses need to be serialized. */
- if (!tx.wit.IsNull()) {
- flags |= 1;
- }
- }
- if (flags) {
- /* Use extended format in case witnesses are to be serialized. */
- std::vector<CTxIn> vinDummy;
- READWRITE(vinDummy);
- READWRITE(flags);
+ /* We read a non-empty vin. Assume a normal vout follows. */
+ s >> tx.vout;
+ }
+ if ((flags & 1) && fAllowWitness) {
+ /* The witness flag is present, and we support witnesses. */
+ flags ^= 1;
+ tx.wit.vtxinwit.resize(tx.vin.size());
+ s >> tx.wit;
+ }
+ if (flags) {
+ /* Unknown flag in the serialization */
+ throw std::ios_base::failure("Unknown transaction optional data");
+ }
+ s >> tx.nLockTime;
+}
+
+template<typename Stream, typename TxType>
+inline void SerializeTransaction(const TxType& tx, Stream& s) {
+ const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
+
+ s << tx.nVersion;
+ unsigned char flags = 0;
+ // Consistency check
+ assert(tx.wit.vtxinwit.size() <= tx.vin.size());
+ if (fAllowWitness) {
+ /* Check whether witnesses need to be serialized. */
+ if (!tx.wit.IsNull()) {
+ flags |= 1;
}
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- if (flags & 1) {
- const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
- READWRITE(tx.wit);
+ }
+ if (flags) {
+ /* Use extended format in case witnesses are to be serialized. */
+ std::vector<CTxIn> vinDummy;
+ s << vinDummy;
+ s << flags;
+ }
+ s << tx.vin;
+ s << tx.vout;
+ if (flags & 1) {
+ for (size_t i = 0; i < tx.vin.size(); i++) {
+ if (i < tx.wit.vtxinwit.size()) {
+ s << tx.wit.vtxinwit[i];
+ } else {
+ s << CTxInWitness();
+ }
}
}
- READWRITE(*const_cast<uint32_t*>(&tx.nLockTime));
+ s << tx.nLockTime;
}
+
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
-private:
- /** Memory only. */
- const uint256 hash;
-
public:
// Default transaction version.
static const int32_t CURRENT_VERSION=1;
@@ -374,6 +382,13 @@ public:
CTxWitness wit; // Not const: can change without invalidating the txid cache
const uint32_t nLockTime;
+private:
+ /** Memory only. */
+ const uint256 hash;
+
+ uint256 ComputeHash() const;
+
+public:
/** Construct a CTransaction that qualifies as IsNull() */
CTransaction();
@@ -381,18 +396,13 @@ public:
CTransaction(const CMutableTransaction &tx);
CTransaction(CMutableTransaction &&tx);
- CTransaction& operator=(const CTransaction& tx);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- SerializeTransaction(*this, s, ser_action);
- if (ser_action.ForRead()) {
- UpdateHash();
- }
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ SerializeTransaction(*this, s);
}
+ /** This deserializing constructor is provided instead of an Unserialize method.
+ * Unserialize is not possible, since it would require overwriting const fields. */
template <typename Stream>
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
@@ -417,7 +427,7 @@ public:
// Compute modified tx size for priority calculation (optionally given tx size)
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
-
+
/**
* Get the total transaction size in bytes, including witness data.
* "Total Size" defined in BIP141 and BIP144.
@@ -441,8 +451,6 @@ public:
}
std::string ToString() const;
-
- void UpdateHash() const;
};
/** A mutable version of CTransaction. */
@@ -457,11 +465,15 @@ struct CMutableTransaction
CMutableTransaction();
CMutableTransaction(const CTransaction& tx);
- ADD_SERIALIZE_METHODS;
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ SerializeTransaction(*this, s);
+ }
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- SerializeTransaction(*this, s, ser_action);
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ UnserializeTransaction(*this, s);
}
template <typename Stream>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6112a1d256..54ed867de0 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -199,8 +199,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
labelWalletEncryptionIcon = new QLabel();
labelWalletHDStatusIcon = new QLabel();
- connectionsControl = new NetworkToggleStatusBarControl();
- labelBlocksIcon = new QLabel();
+ connectionsControl = new GUIUtil::ClickableLabel();
+ labelBlocksIcon = new GUIUtil::ClickableLabel();
if(enableWallet)
{
frameBlocksLayout->addStretch();
@@ -244,10 +244,14 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
// Subscribe to notifications from core
subscribeToCoreSignals();
+ connect(connectionsControl, SIGNAL(clicked(QPoint)), this, SLOT(toggleNetworkActive()));
+
modalOverlay = new ModalOverlay(this->centralWidget());
#ifdef ENABLE_WALLET
- if(enableWallet)
+ if(enableWallet) {
connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this, SLOT(showModalOverlay()));
+ connect(labelBlocksIcon, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay()));
+ }
#endif
}
@@ -490,7 +494,6 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
- connectionsControl->setClientModel(_clientModel);
OptionsModel* optionsModel = _clientModel->getOptionsModel();
if(optionsModel)
@@ -517,7 +520,6 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
walletFrame->setClientModel(nullptr);
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(nullptr);
- connectionsControl->setClientModel(nullptr);
}
}
@@ -1171,6 +1173,13 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
uiInterface.ThreadSafeQuestion.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
}
+void BitcoinGUI::toggleNetworkActive()
+{
+ if (clientModel) {
+ clientModel->setNetworkActive(!clientModel->getNetworkActive());
+ }
+}
+
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
optionsModel(0),
menu(0)
@@ -1244,16 +1253,3 @@ void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
optionsModel->setDisplayUnit(action->data());
}
}
-
-void NetworkToggleStatusBarControl::mousePressEvent(QMouseEvent *event)
-{
- if (clientModel) {
- clientModel->setNetworkActive(!clientModel->getNetworkActive());
- }
-}
-
-/** Lets the control know about the Client Model */
-void NetworkToggleStatusBarControl::setClientModel(ClientModel *_clientModel)
-{
- this->clientModel = _clientModel;
-}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 1b02e77fc4..59540bfe6b 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -26,7 +26,6 @@ class PlatformStyle;
class RPCConsole;
class SendCoinsRecipient;
class UnitDisplayStatusBarControl;
-class NetworkToggleStatusBarControl;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
@@ -86,7 +85,7 @@ private:
UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelWalletEncryptionIcon;
QLabel *labelWalletHDStatusIcon;
- NetworkToggleStatusBarControl *connectionsControl;
+ QLabel *connectionsControl;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
QProgressBar *progressBar;
@@ -238,6 +237,9 @@ private Q_SLOTS:
/** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */
void setTrayIconVisible(bool);
+ /** Toggle networking */
+ void toggleNetworkActive();
+
void showModalOverlay();
};
@@ -270,17 +272,4 @@ private Q_SLOTS:
void onMenuSelection(QAction* action);
};
-class NetworkToggleStatusBarControl : public QLabel
-{
- Q_OBJECT
-
-public:
- void setClientModel(ClientModel *clientModel);
-protected:
- void mousePressEvent(QMouseEvent *event);
-
-private:
- ClientModel *clientModel;
-};
-
#endif // BITCOIN_QT_BITCOINGUI_H
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index a4bb2f77fe..e63a9cc7b8 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -12,6 +12,7 @@
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
+#include "validation.h"
#include "net.h"
#include "txmempool.h"
#include "ui_interface.h"
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index d77db39b3c..4d34bf95e9 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -15,7 +15,7 @@
#include "wallet/coincontrol.h"
#include "init.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "wallet/wallet.h"
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
@@ -470,21 +470,21 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nQuantity++;
// Amount
- nAmount += out.tx->vout[out.i].nValue;
+ nAmount += out.tx->tx->vout[out.i].nValue;
// Priority
- dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
+ dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1);
// Bytes
CTxDestination address;
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
- if (out.tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
+ if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
fWitness = true;
}
- else if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
+ else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address))
{
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
@@ -677,7 +677,7 @@ void CoinControlDialog::updateView()
CAmount nSum = 0;
int nChildren = 0;
BOOST_FOREACH(const COutput& out, coins.second) {
- nSum += out.tx->vout[out.i].nValue;
+ nSum += out.tx->tx->vout[out.i].nValue;
nChildren++;
CCoinControlWidgetItem *itemOutput;
@@ -689,7 +689,7 @@ void CoinControlDialog::updateView()
// address
CTxDestination outputAddress;
QString sAddress = "";
- if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
+ if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
@@ -714,8 +714,8 @@ void CoinControlDialog::updateView()
}
// amount
- itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
- itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->vout[out.i].nValue)); // padding so that sorting works correctly
+ itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
+ itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 33db9f8938..0a8afa2e76 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -1064,7 +1064,7 @@
<number>0</number>
</property>
<property name="maximum">
- <number>24</number>
+ <number>23</number>
</property>
<property name="pageStep">
<number>1</number>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 130cfc6e7d..4806e41439 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -11,7 +11,7 @@
#include "primitives/transaction.h"
#include "init.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "protocol.h"
#include "script/script.h"
#include "script/standard.h"
@@ -55,6 +55,7 @@
#include <QSettings>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
+#include <QMouseEvent>
#if QT_VERSION < 0x050000
#include <QUrl>
@@ -986,4 +987,10 @@ QString formateNiceTimeOffset(qint64 secs)
}
return timeBehindText;
}
+
+void ClickableLabel::mousePressEvent(QMouseEvent *event)
+{
+ Q_EMIT clicked(event->pos());
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 8f1f3fbb2c..9a17d24f07 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -14,6 +14,7 @@
#include <QProgressBar>
#include <QString>
#include <QTableView>
+#include <QLabel>
#include <boost/filesystem.hpp>
@@ -215,6 +216,19 @@ namespace GUIUtil
typedef QProgressBar ProgressBar;
#endif
+ class ClickableLabel : public QLabel
+ {
+ Q_OBJECT
+
+ Q_SIGNALS:
+ /** Emitted when the label is clicked. The relative mouse coordinates of the click are
+ * passed to the signal.
+ */
+ void clicked(const QPoint& point);
+ protected:
+ void mousePressEvent(QMouseEvent *event);
+ };
+
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 588059d0c5..2d5e9ff686 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -13,7 +13,7 @@
#include "guiutil.h"
#include "optionsmodel.h"
-#include "main.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
+#include "validation.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index f82e153b67..d48c4d91e7 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -13,7 +13,7 @@
#include "amount.h"
#include "init.h"
-#include "main.h" // For DEFAULT_SCRIPTCHECK_THREADS
+#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS
#include "net.h"
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 229752cad2..d5f0156d6c 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -10,7 +10,7 @@
#include "base58.h"
#include "chainparams.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "ui_interface.h"
#include "util.h"
#include "wallet/wallet.h"
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index c0b4900285..dd4bd55396 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -8,6 +8,7 @@
#include "guiconstants.h"
#include "guiutil.h"
+#include "validation.h" // for cs_main
#include "sync.h"
#include <QDebug>
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index af34b147b1..3e4f0a68c4 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_PEERTABLEMODEL_H
#define BITCOIN_QT_PEERTABLEMODEL_H
-#include "main.h" // For CNodeStateStats
+#include "net_processing.h" // For CNodeStateStats
#include "net.h"
#include <QAbstractTableModel>
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 57b2179435..13210a1adc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -17,7 +17,7 @@
#include "base58.h"
#include "wallet/coincontrol.h"
-#include "main.h" // mempool and minRelayTxFee
+#include "validation.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
#include "wallet/wallet.h"
@@ -175,7 +175,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
- ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 1);
+ ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2);
else
ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
}
@@ -241,7 +241,7 @@ void SendCoinsDialog::on_sendButton_clicked()
if (model->getOptionsModel()->getCoinControlFeatures())
ctrl = *CoinControlDialog::coinControl;
if (ui->radioSmartFee->isChecked())
- ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
+ ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
else
ctrl.nConfirmTarget = 0;
@@ -601,7 +601,7 @@ void SendCoinsDialog::updateGlobalFeeVariables()
{
if (ui->radioSmartFee->isChecked())
{
- int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
+ int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
payTxFee = CFeeRate(0);
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
@@ -646,7 +646,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
if(!model || !model->getOptionsModel())
return;
- int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
+ int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 3e42f3a7b0..e28a6b47af 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -12,7 +12,7 @@
#include "base58.h"
#include "init.h"
-#include "main.h" // For strMessageMagic
+#include "validation.h" // For strMessageMagic
#include "wallet/wallet.h"
#include <string>
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 3dae33bafb..69757f9a93 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -6,7 +6,7 @@
#include "chainparams.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "rpc/register.h"
#include "rpc/server.h"
#include "rpcconsole.h"
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 65144e7865..e7d03a6119 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -11,7 +11,7 @@
#include "base58.h"
#include "consensus/consensus.h"
-#include "main.h"
+#include "validation.h"
#include "script/script.h"
#include "timedata.h"
#include "util.h"
@@ -26,10 +26,10 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
AssertLockHeld(cs_main);
if (!CheckFinalTx(wtx))
{
- if (wtx.nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height());
+ if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
+ return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height());
else
- return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
+ return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
}
else
{
@@ -133,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Coinbase
//
CAmount nUnmatured = 0;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
@@ -152,14 +152,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
else
{
isminetype fAllFromMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
if(fAllFromMe > mine) fAllFromMe = mine;
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(fAllToMe > mine) fAllToMe = mine;
@@ -173,7 +173,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Debit
//
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
// Ignore change
isminetype toSelf = wallet->IsMine(txout);
@@ -212,7 +212,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
}
- CAmount nTxFee = nDebit - wtx.GetValueOut();
+ CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
if (nTxFee > 0)
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
}
@@ -221,10 +221,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Mixed debit transaction
//
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if (wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if (wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
}
@@ -241,7 +241,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
- strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.GetTotalSize()) + " bytes<br>";
+ strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example)
@@ -276,20 +276,20 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (fDebug)
{
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if(wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if(wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
- strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
+ strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
strHTML += "<br><b>" + tr("Inputs") + ":</b>";
strHTML += "<ul>";
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
COutPoint prevout = txin.prevout;
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 4fe47181f6..a1d1422317 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -6,7 +6,7 @@
#include "base58.h"
#include "consensus/consensus.h"
-#include "main.h"
+#include "validation.h"
#include "timedata.h"
#include "wallet/wallet.h"
@@ -47,7 +47,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
// Credit
//
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(mine)
@@ -83,7 +83,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
@@ -91,7 +91,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
@@ -112,11 +112,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
// Debit
//
- CAmount nTxFee = nDebit - wtx.GetValueOut();
+ CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
- for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
+ for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
{
- const CTxOut& txout = wtx.vout[nOut];
+ const CTxOut& txout = wtx.tx->vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
sub.involvesWatchAddress = involvesWatchAddress;
@@ -190,15 +190,15 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
if (!CheckFinalTx(wtx))
{
- if (wtx.nLockTime < LOCKTIME_THRESHOLD)
+ if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = wtx.nLockTime - chainActive.Height();
+ status.open_for = wtx.tx->nLockTime - chainActive.Height();
}
else
{
status.status = TransactionStatus::OpenUntilDate;
- status.open_for = wtx.nLockTime;
+ status.open_for = wtx.tx->nLockTime;
}
}
// For generated transactions, determine maturity
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 52261ff04b..da0742aa6a 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -14,7 +14,7 @@
#include "walletmodel.h"
#include "core_io.h"
-#include "main.h"
+#include "validation.h"
#include "sync.h"
#include "uint256.h"
#include "util.h"
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 3490d1c1cc..a78fc90d2c 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -14,9 +14,11 @@
#include "base58.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
+#include "net.h" // for g_connman
#include "sync.h"
#include "ui_interface.h"
+#include "util.h" // for GetBoolArg
#include "wallet/wallet.h"
#include "wallet/walletdb.h" // for BackupWallet
@@ -65,7 +67,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
if(out.fSpendable)
- nBalance += out.tx->vout[out.i].nValue;
+ nBalance += out.tx->tx->vout[out.i].nValue;
return nBalance;
}
@@ -607,7 +609,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true);
- if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
+ if (outpoint.n < out.tx->tx->vout.size() && wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
vCoins.push_back(out);
}
@@ -615,14 +617,14 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
{
COutput cout = out;
- while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
+ while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0]))
{
- if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
- cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true);
+ if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break;
+ cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0, true, true);
}
CTxDestination address;
- if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
+ if(!out.fSpendable || !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address))
continue;
mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
}
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index fdec6a1c86..f9a95bed42 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -64,7 +64,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
if (out.amount() <= 0) continue;
if (i == nChangePosRet)
i++;
- subtotal += walletTransaction->vout[i].nValue;
+ subtotal += walletTransaction->tx->vout[i].nValue;
i++;
}
rcp.amount = subtotal;
@@ -73,7 +73,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
if (i == nChangePosRet)
i++;
- rcp.amount = walletTransaction->vout[i].nValue;
+ rcp.amount = walletTransaction->tx->vout[i].nValue;
i++;
}
}
diff --git a/src/rest.cpp b/src/rest.cpp
index 90cca6f480..6379061f8f 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -7,7 +7,7 @@
#include "chainparams.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
-#include "main.h"
+#include "validation.h"
#include "httpserver.h"
#include "rpc/server.h"
#include "streams.h"
@@ -363,7 +363,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
if (!ParseHashStr(hashStr, hash))
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
- CTransaction tx;
+ CTransactionRef tx;
uint256 hashBlock = uint256();
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
@@ -388,7 +388,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
case RF_JSON: {
UniValue objTx(UniValue::VOBJ);
- TxToJSON(tx, hashBlock, objTx);
+ TxToJSON(*tx, hashBlock, objTx);
string strJSON = objTx.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 733d14d24e..e6d80f06a3 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -9,7 +9,7 @@
#include "checkpoints.h"
#include "coins.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
@@ -1319,7 +1319,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
}
if (state.IsValid()) {
- ActivateBestChain(state, Params(), NULL);
+ ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
@@ -1357,7 +1357,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
}
CValidationState state;
- ActivateBestChain(state, Params(), NULL);
+ ActivateBestChain(state, Params());
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index f3cd1fbf0b..cb22dec342 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -12,7 +12,7 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "net.h"
#include "pow.h"
@@ -131,7 +131,8 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
if (pblock->nNonce == nInnerLoopCount) {
continue;
}
- if (!ProcessNewBlock(Params(), pblock, true, NULL, NULL))
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ if (!ProcessNewBlock(Params(), shared_pblock, true, NULL, NULL))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -728,7 +729,8 @@ UniValue submitblock(const JSONRPCRequest& request)
+ HelpExampleRpc("submitblock", "\"mydata\"")
);
- CBlock block;
+ std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
+ CBlock& block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
@@ -758,7 +760,7 @@ UniValue submitblock(const JSONRPCRequest& request)
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(Params(), &block, true, NULL, NULL);
+ bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL, NULL);
UnregisterValidationInterface(&sc);
if (fBlockPresent)
{
@@ -785,6 +787,8 @@ UniValue estimatefee(const JSONRPCRequest& request)
"\n"
"A negative value is returned if not enough transactions and blocks\n"
"have been observed to make an estimate.\n"
+ "-1 is always returned for nblocks == 1 as it is impossible to calculate\n"
+ "a fee that is high enough to get reliably included in the next block.\n"
"\nExample:\n"
+ HelpExampleCli("estimatefee", "6")
);
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 3193985803..2aaee7f3f5 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -6,7 +6,7 @@
#include "base58.h"
#include "clientversion.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "netbase.h"
#include "rpc/server.h"
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index f57ba76d3a..53c0f993dc 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -6,8 +6,9 @@
#include "chainparams.h"
#include "clientversion.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
+#include "net_processing.h"
#include "netbase.h"
#include "protocol.h"
#include "sync.h"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 370c021ea6..48769a5335 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,7 +10,7 @@
#include "core_io.h"
#include "init.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "merkleblock.h"
#include "net.h"
#include "policy/policy.h"
@@ -218,19 +218,19 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
}
}
- CTransaction tx;
+ CTransactionRef tx;
uint256 hashBlock;
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
- string strHex = EncodeHexTx(tx);
+ string strHex = EncodeHexTx(*tx);
if (!fVerbose)
return strHex;
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", strHex));
- TxToJSON(tx, hashBlock, result);
+ TxToJSON(*tx, hashBlock, result);
return result;
}
@@ -289,7 +289,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
if (pblockindex == NULL)
{
- CTransaction tx;
+ CTransactionRef tx;
if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
if (!mapBlockIndex.count(hashBlock))
@@ -520,13 +520,13 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
LOCK(cs_main);
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
- CTransaction tx;
+ CMutableTransaction mtx;
- if (!DecodeHexTx(tx, request.params[0].get_str(), true))
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
- TxToJSON(tx, uint256(), result);
+ TxToJSON(CTransaction(std::move(mtx)), uint256(), result);
return result;
}
@@ -883,9 +883,10 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
// parse hex string from parameter
- CTransaction tx;
- if (!DecodeHexTx(tx, request.params[0].get_str()))
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ CTransaction tx(std::move(mtx));
uint256 hashTx = tx.GetHash();
bool fLimitFree = false;
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 069ac55bfb..036d6ca7bf 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -85,8 +85,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
}
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
if (nIn >= tx.vin.size())
return set_error(err, bitcoinconsensus_ERR_TX_INDEX);
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index 6eed636080..1a818a5756 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -6,8 +6,8 @@
#include "chainparams.h"
#include "keystore.h"
-#include "main.h"
#include "net.h"
+#include "net_processing.h"
#include "pow.h"
#include "script/sign.h"
#include "serialize.h"
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 25fb9ea2b7..b15ff9e44b 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -30,15 +30,15 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
- BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
@@ -51,9 +51,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
filter.clear();
- BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!");
+ BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter should be empty!");
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
@@ -62,15 +62,15 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
- BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
@@ -114,16 +114,14 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
BOOST_AUTO_TEST_CASE(bloom_match)
{
// Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
- CTransaction tx;
CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION);
- stream >> tx;
+ CTransaction tx(deserialize, stream);
// and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION);
- CTransaction spendingTx;
- spendStream >> spendingTx;
+ CTransaction spendingTx(deserialize, spendStream);
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(uint256S("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 82de302053..79dea00e46 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -8,7 +8,7 @@
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"
-#include "main.h"
+#include "validation.h"
#include "consensus/validation.h"
#include <vector>
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index dbfbdd934f..697fbf792c 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -3,7 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
+#include "net.h"
#include "test/test_bitcoin.h"
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 85a2e907c2..bc1bdd8874 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -7,7 +7,7 @@
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "pubkey.h"
#include "script/standard.h"
@@ -223,7 +223,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
- BOOST_CHECK(ProcessNewBlock(chainparams, pblock, true, NULL, NULL));
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, NULL, NULL));
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 7dc8f226c9..c57feaec90 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -95,17 +95,22 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Highest feerate is 10*baseRate and gets in all blocks,
// second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
// third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
- // so estimateFee(1) should return 10*baseRate.
+ // so estimateFee(1) would return 10*baseRate but is hardcoded to return failure
// Second highest feerate has 100% chance of being included by 2 blocks,
// so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
- if (i > 1) { // Fee estimates should be monotonically decreasing
+ if (i > 2) { // Fee estimates should be monotonically decreasing
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
}
int mult = 11-i;
- BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
+ if (i > 1) {
+ BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
+ }
+ else {
+ BOOST_CHECK(origFeeEst[i-1] == CFeeRate(0).GetFeePerK());
+ }
}
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
@@ -113,7 +118,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
while (blocknum < 250)
mpool.removeForBlock(block, ++blocknum);
- for (int i = 1; i < 10;i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
@@ -151,7 +157,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
mpool.removeForBlock(block, 265);
block.clear();
- for (int i = 1; i < 10;i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
@@ -172,7 +179,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum);
block.clear();
}
- for (int i = 1; i < 10; i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10; i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
}
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 1a01593a8e..789cc70d7f 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -5,7 +5,7 @@
#include "core_io.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "script/script.h"
#include "script/script_error.h"
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 3169afb13a..eb324d5a6b 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -276,7 +276,7 @@ private:
//! The Witness embedded script
CScript witscript;
CScriptWitness scriptWitness;
- CTransaction creditTx;
+ CTransactionRef creditTx;
CMutableTransaction spendTx;
bool havePush;
std::vector<unsigned char> push;
@@ -319,8 +319,8 @@ public:
redeemscript = scriptPubKey;
scriptPubKey = CScript() << OP_HASH160 << ToByteVector(CScriptID(redeemscript)) << OP_EQUAL;
}
- creditTx = BuildCreditingTransaction(scriptPubKey, nValue);
- spendTx = BuildSpendingTransaction(CScript(), CScriptWitness(), creditTx);
+ creditTx = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, nValue));
+ spendTx = BuildSpendingTransaction(CScript(), CScriptWitness(), *creditTx);
}
TestBuilder& ScriptError(ScriptError_t err)
@@ -421,7 +421,7 @@ public:
{
TestBuilder copy = *this; // Make a copy so we can rollback the push.
DoPush();
- DoTest(creditTx.vout[0].scriptPubKey, spendTx.vin[0].scriptSig, scriptWitness, flags, comment, scriptError, nValue);
+ DoTest(creditTx->vout[0].scriptPubKey, spendTx.vin[0].scriptSig, scriptWitness, flags, comment, scriptError, nValue);
*this = copy;
return *this;
}
@@ -447,7 +447,7 @@ public:
array.push_back(wit);
}
array.push_back(FormatScript(spendTx.vin[0].scriptSig));
- array.push_back(FormatScript(creditTx.vout[0].scriptPubKey));
+ array.push_back(FormatScript(creditTx->vout[0].scriptPubKey));
array.push_back(FormatScriptFlags(flags));
array.push_back(FormatScriptError((ScriptError_t)scriptError));
array.push_back(comment);
@@ -461,7 +461,7 @@ public:
const CScript& GetScriptPubKey()
{
- return creditTx.vout[0].scriptPubKey;
+ return creditTx->vout[0].scriptPubKey;
}
};
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index bbadf57957..1dc86eb116 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -21,10 +21,10 @@ protected:
bool boolval;
std::string stringval;
const char* charstrval;
- CTransaction txval;
+ CTransactionRef txval;
public:
CSerializeMethodsTestSingle() = default;
- CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(txvalin){}
+ CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(MakeTransactionRef(txvalin)){}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@@ -42,7 +42,7 @@ public:
boolval == rhs.boolval && \
stringval == rhs.stringval && \
strcmp(charstrval, rhs.charstrval) == 0 && \
- txval == rhs.txval;
+ *txval == *rhs.txval;
}
};
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 3bc8341b02..a524f5b946 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -5,7 +5,7 @@
#include "consensus/validation.h"
#include "data/sighash.json.h"
#include "hash.h"
-#include "main.h" // For CheckTransaction
+#include "validation.h" // For CheckTransaction
#include "script/interpreter.h"
#include "script/script.h"
#include "serialize.h"
@@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
std::string raw_tx, raw_script, sigHashHex;
int nIn, nHashType;
uint256 sh;
- CTransaction tx;
+ CTransactionRef tx;
CScript scriptCode = CScript();
try {
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
stream >> tx;
CValidationState state;
- BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
+ BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);
BOOST_CHECK(state.IsValid());
std::vector<unsigned char> raw = ParseHex(raw_script);
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
continue;
}
- sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, SIGVERSION_BASE);
+ sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SIGVERSION_BASE);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
}
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index e8a63ae60c..2e974cc612 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.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 "main.h"
+#include "validation.h"
#include "pubkey.h"
#include "key.h"
#include "script/script.h"
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 6cbe314a76..139389117a 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -10,8 +10,9 @@
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
+#include "net_processing.h"
#include "pubkey.h"
#include "random.h"
#include "txdb.h"
@@ -126,7 +127,8 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
- ProcessNewBlock(chainparams, &block, true, NULL, NULL);
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
+ ProcessNewBlock(chainparams, shared_pblock, true, NULL, NULL);
CBlock result = block;
return result;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 34d9547f3d..ae5406ef7e 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -12,7 +12,7 @@
#include "core_io.h"
#include "key.h"
#include "keystore.h"
-#include "main.h" // For CheckTransaction
+#include "validation.h" // For CheckTransaction
#include "policy/policy.h"
#include "script/script.h"
#include "script/sign.h"
@@ -150,8 +150,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
@@ -236,8 +235,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION );
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
CValidationState state;
fValid = CheckTransaction(tx, state) && state.IsValid();
@@ -346,7 +344,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
}
-void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransaction& output, CMutableTransaction& input, bool success = true)
+void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
{
CMutableTransaction outputm;
outputm.nVersion = 1;
@@ -360,22 +358,22 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
ssout << outputm;
ssout >> output;
- assert(output.vin.size() == 1);
- assert(output.vin[0] == outputm.vin[0]);
- assert(output.vout.size() == 1);
- assert(output.vout[0] == outputm.vout[0]);
- assert(output.wit.vtxinwit.size() == 0);
+ assert(output->vin.size() == 1);
+ assert(output->vin[0] == outputm.vin[0]);
+ assert(output->vout.size() == 1);
+ assert(output->vout[0] == outputm.vout[0]);
+ assert(output->wit.vtxinwit.size() == 0);
CMutableTransaction inputm;
inputm.nVersion = 1;
inputm.vin.resize(1);
- inputm.vin[0].prevout.hash = output.GetHash();
+ inputm.vin[0].prevout.hash = output->GetHash();
inputm.vin[0].prevout.n = 0;
inputm.wit.vtxinwit.resize(1);
inputm.vout.resize(1);
inputm.vout[0].nValue = 1;
inputm.vout[0].scriptPubKey = CScript();
- bool ret = SignSignature(keystore, output, inputm, 0, SIGHASH_ALL);
+ bool ret = SignSignature(keystore, *output, inputm, 0, SIGHASH_ALL);
assert(ret == success);
CDataStream ssin(SER_NETWORK, PROTOCOL_VERSION);
ssin << inputm;
@@ -393,11 +391,11 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
}
}
-void CheckWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags, bool success)
+void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
{
ScriptError error;
CTransaction inputi(input);
- bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error);
+ bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error);
assert(ret == success);
}
@@ -466,10 +464,10 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
assert(hashSigned);
}
- CTransaction tx;
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
- WithOrVersion(&ssout, 0) << mtx;
- WithOrVersion(&ssout, 0) >> tx;
+ auto vstream = WithOrVersion(&ssout, 0);
+ vstream << mtx;
+ CTransaction tx(deserialize, vstream);
// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
@@ -547,7 +545,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
keystore2.AddCScript(GetScriptForWitness(scriptMulti));
keystore2.AddKeyPubKey(key3, pubkey3);
- CTransaction output1, output2;
+ CTransactionRef output1, output2;
CMutableTransaction input1, input2;
SignatureData sigdata;
@@ -639,8 +637,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, 0, false);
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
CheckWithFlag(output2, input2, 0, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
@@ -650,8 +648,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -662,8 +660,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForWitness(scriptMulti), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -674,8 +672,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
}
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 76e4e7a4be..55b4d28fb2 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -4,7 +4,7 @@
#include "consensus/validation.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "pubkey.h"
#include "txmempool.h"
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index c05d593ed6..bae0eff7e5 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -7,7 +7,7 @@
#include "test/test_bitcoin.h"
#include "test/test_random.h"
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
#include "consensus/params.h"
#include <boost/test/unit_test.hpp>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 417a88cbef..c035a84db5 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -8,7 +8,7 @@
#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "policy/fees.h"
#include "streams.h"
diff --git a/src/main.cpp b/src/validation.cpp
index e2a1f31228..a541978c85 100644
--- a/src/main.cpp
+++ b/src/validation.cpp
@@ -3,11 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "main.h"
+#include "validation.h"
-#include "addrman.h"
#include "arith_uint256.h"
-#include "blockencodings.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -16,10 +14,6 @@
#include "consensus/validation.h"
#include "hash.h"
#include "init.h"
-#include "merkleblock.h"
-#include "net.h"
-#include "netmessagemaker.h"
-#include "netbase.h"
#include "policy/fees.h"
#include "policy/policy.h"
#include "pow.h"
@@ -29,6 +23,7 @@
#include "script/script.h"
#include "script/sigcache.h"
#include "script/standard.h"
+#include "timedata.h"
#include "tinyformat.h"
#include "txdb.h"
#include "txmempool.h"
@@ -65,7 +60,6 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CBlockIndex *pindexBestHeader = NULL;
-int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last received a block
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
@@ -88,25 +82,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CTxMemPool mempool(::minRelayTxFee);
-FeeFilterRounder filterRounder(::minRelayTxFee);
-
-struct IteratorComparator
-{
- template<typename I>
- bool operator()(const I& a, const I& b)
- {
- return &(*a) < &(*b);
- }
-};
-
-struct COrphanTx {
- CTransaction tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
-};
-map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
-map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
-void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
static void CheckBlockIndex(const Consensus::Params& consensusParams);
@@ -115,8 +90,6 @@ CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n";
-static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
-
// Internal stuff
namespace {
@@ -149,8 +122,6 @@ namespace {
* missing the data for the block.
*/
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
- /** Number of nodes with fSyncStarted. */
- int nSyncStarted = 0;
/** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
@@ -177,527 +148,13 @@ namespace {
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;
- /**
- * Sources of received blocks, saved to be able to send them reject
- * messages or ban them when processing happens afterwards. Protected by
- * cs_main.
- * Set mapBlockSource[hash].second to false if the node should not be
- * punished if the block is invalid.
- */
- map<uint256, std::pair<NodeId, bool>> mapBlockSource;
-
- /**
- * Filter for transactions that were recently rejected by
- * AcceptToMemoryPool. These are not rerequested until the chain tip
- * changes, at which point the entire filter is reset. Protected by
- * cs_main.
- *
- * Without this filter we'd be re-requesting txs from each of our peers,
- * increasing bandwidth consumption considerably. For instance, with 100
- * peers, half of which relay a tx we don't accept, that might be a 50x
- * bandwidth increase. A flooding attacker attempting to roll-over the
- * filter using minimum-sized, 60byte, transactions might manage to send
- * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
- * two minute window to send invs to us.
- *
- * Decreasing the false positive rate is fairly cheap, so we pick one in a
- * million to make it highly unlikely for users to have issues with this
- * filter.
- *
- * Memory used: 1.3 MB
- */
- std::unique_ptr<CRollingBloomFilter> recentRejects;
- uint256 hashRecentRejectsChainTip;
-
- /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
- struct QueuedBlock {
- uint256 hash;
- CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
- };
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
-
- /** Stack of nodes which we have set to announce using compact blocks */
- list<NodeId> lNodesAnnouncingHeaderAndIDs;
-
- /** Number of preferable block download peers. */
- int nPreferredDownload = 0;
-
/** Dirty block index entries. */
set<CBlockIndex*> setDirtyBlockIndex;
/** Dirty block file entries. */
set<int> setDirtyFileInfo;
-
- /** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads = 0;
-
- /** Relay map, protected by cs_main. */
- typedef std::map<uint256, CTransactionRef> MapRelay;
- MapRelay mapRelay;
- /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
- std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
} // anon namespace
-//////////////////////////////////////////////////////////////////////////////
-//
-// Registration of network node signals.
-//
-
-namespace {
-
-struct CBlockReject {
- unsigned char chRejectCode;
- string strRejectReason;
- uint256 hashBlock;
-};
-
-/**
- * Maintain validation-specific state about nodes, protected by cs_main, instead
- * by CNode's own locks. This simplifies asynchronous operation, where
- * processing of incoming data is done after the ProcessMessage call returns,
- * and we're no longer holding the node's locks.
- */
-struct CNodeState {
- //! The peer's address
- const CService address;
- //! Whether we have a fully established connection.
- bool fCurrentlyConnected;
- //! Accumulated misbehaviour score for this peer.
- int nMisbehavior;
- //! Whether this peer should be disconnected and banned (unless whitelisted).
- bool fShouldBan;
- //! String name of this peer (debugging/logging purposes).
- const std::string name;
- //! List of asynchronously-determined block rejections to notify this peer about.
- std::vector<CBlockReject> rejects;
- //! The best known block we know this peer has announced.
- CBlockIndex *pindexBestKnownBlock;
- //! The hash of the last unknown block this peer has announced.
- uint256 hashLastUnknownBlock;
- //! The last full block we both have.
- CBlockIndex *pindexLastCommonBlock;
- //! The best header we have sent our peer.
- CBlockIndex *pindexBestHeaderSent;
- //! Length of current-streak of unconnecting headers announcements
- int nUnconnectingHeaders;
- //! Whether we've started headers synchronization with this peer.
- bool fSyncStarted;
- //! Since when we're stalling block download progress (in microseconds), or 0.
- int64_t nStallingSince;
- list<QueuedBlock> vBlocksInFlight;
- //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
- int64_t nDownloadingSince;
- int nBlocksInFlight;
- int nBlocksInFlightValidHeaders;
- //! Whether we consider this a preferred download peer.
- bool fPreferredDownload;
- //! Whether this peer wants invs or headers (when possible) for block announcements.
- bool fPreferHeaders;
- //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
- bool fPreferHeaderAndIDs;
- /**
- * Whether this peer will send us cmpctblocks if we request them.
- * This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
- * but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
- */
- bool fProvidesHeaderAndIDs;
- //! Whether this peer can give us witnesses
- bool fHaveWitness;
- //! Whether this peer wants witnesses in cmpctblocks/blocktxns
- bool fWantsCmpctWitness;
- /**
- * If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
- * otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
- */
- bool fSupportsDesiredCmpctVersion;
-
- CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
- fCurrentlyConnected = false;
- nMisbehavior = 0;
- fShouldBan = false;
- pindexBestKnownBlock = NULL;
- hashLastUnknownBlock.SetNull();
- pindexLastCommonBlock = NULL;
- pindexBestHeaderSent = NULL;
- nUnconnectingHeaders = 0;
- fSyncStarted = false;
- nStallingSince = 0;
- nDownloadingSince = 0;
- nBlocksInFlight = 0;
- nBlocksInFlightValidHeaders = 0;
- fPreferredDownload = false;
- fPreferHeaders = false;
- fPreferHeaderAndIDs = false;
- fProvidesHeaderAndIDs = false;
- fHaveWitness = false;
- fWantsCmpctWitness = false;
- fSupportsDesiredCmpctVersion = false;
- }
-};
-
-/** Map maintaining per-node state. Requires cs_main. */
-map<NodeId, CNodeState> mapNodeState;
-
-// Requires cs_main.
-CNodeState *State(NodeId pnode) {
- map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
- if (it == mapNodeState.end())
- return NULL;
- return &it->second;
-}
-
-void UpdatePreferredDownload(CNode* node, CNodeState* state)
-{
- nPreferredDownload -= state->fPreferredDownload;
-
- // Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
-
- nPreferredDownload += state->fPreferredDownload;
-}
-
-void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
-{
- ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
- uint64_t nonce = pnode->GetLocalNonce();
- int nNodeStartingHeight = pnode->GetMyStartingHeight();
- NodeId nodeid = pnode->GetId();
- CAddress addr = pnode->addr;
-
- CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
- CAddress addrMe = CAddress(CService(), nLocalNodeServices);
-
- connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
-
- if (fLogIPs)
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
- else
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), nodeid);
-}
-
-void InitializeNode(CNode *pnode, CConnman& connman) {
- CAddress addr = pnode->addr;
- std::string addrName = pnode->addrName;
- NodeId nodeid = pnode->GetId();
- {
- LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName)));
- }
- if(!pnode->fInbound)
- PushNodeVersion(pnode, connman, GetTime());
-}
-
-void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
- fUpdateConnectionTime = false;
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
-
- if (state->fSyncStarted)
- nSyncStarted--;
-
- if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
- fUpdateConnectionTime = true;
- }
-
- BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
- }
- EraseOrphansFor(nodeid);
- nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
-
- mapNodeState.erase(nodeid);
-
- if (mapNodeState.empty()) {
- // Do a consistency check after the last peer is removed.
- assert(mapBlocksInFlight.empty());
- assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
- }
-}
-
-// Requires cs_main.
-// Returns a bool indicating whether we requested this block.
-// Also used if a block was /not/ received and timed out or started with another peer
-bool MarkBlockAsReceived(const uint256& hash) {
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->nStallingSince = 0;
- mapBlocksInFlight.erase(itInFlight);
- return true;
- }
- return false;
-}
-
-// Requires cs_main.
-// returns false, still setting pit, if the block was already in flight from the same peer
-// pit will only be valid as long as the same cs_main lock is being held
-bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- // Short-circuit most stuff in case its from the same node
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) {
- *pit = &itInFlight->second.second;
- return false;
- }
-
- // Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
-
- list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != NULL, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)});
- state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
- if (state->nBlocksInFlight == 1) {
- // We're starting a block download (batch) from this peer.
- state->nDownloadingSince = GetTimeMicros();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) {
- nPeersWithValidatedDownloads++;
- }
- itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
- *pit = &itInFlight->second.second;
- return true;
-}
-
-/** Check whether the last unknown block a peer advertised is not yet known. */
-void ProcessBlockAvailability(NodeId nodeid) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- if (!state->hashLastUnknownBlock.IsNull()) {
- BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
- if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
- if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
- state->pindexBestKnownBlock = itOld->second;
- state->hashLastUnknownBlock.SetNull();
- }
- }
-}
-
-/** Update tracking information about which blocks a peer is assumed to have. */
-void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- ProcessBlockAvailability(nodeid);
-
- BlockMap::iterator it = mapBlockIndex.find(hash);
- if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
- // An actually better block was announced.
- if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
- state->pindexBestKnownBlock = it->second;
- } else {
- // An unknown block was announced; just assume that the latest one is the best one.
- state->hashLastUnknownBlock = hash;
- }
-}
-
-void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) {
- if (!nodestate->fSupportsDesiredCmpctVersion) {
- // Never ask from peers who can't provide witnesses.
- return;
- }
- if (nodestate->fProvidesHeaderAndIDs) {
- for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
- if (*it == pfrom->GetId()) {
- lNodesAnnouncingHeaderAndIDs.erase(it);
- lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
- return;
- }
- }
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
- if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
- // As per BIP152, we only get 3 of our peers to announce
- // blocks using compact encodings.
- connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){
- connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
- return true;
- });
- lNodesAnnouncingHeaderAndIDs.pop_front();
- }
- fAnnounceUsingCMPCTBLOCK = true;
- connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
- lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
- }
-}
-
-// Requires cs_main
-bool CanDirectFetch(const Consensus::Params &consensusParams)
-{
- return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
-}
-
-// Requires cs_main
-bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
-{
- if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
- return true;
- if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
- return true;
- return false;
-}
-
-/** Find the last common ancestor two blocks have.
- * Both pa and pb must be non-NULL. */
-CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
- if (pa->nHeight > pb->nHeight) {
- pa = pa->GetAncestor(pb->nHeight);
- } else if (pb->nHeight > pa->nHeight) {
- pb = pb->GetAncestor(pa->nHeight);
- }
-
- while (pa != pb && pa && pb) {
- pa = pa->pprev;
- pb = pb->pprev;
- }
-
- // Eventually all chain branches meet at the genesis block.
- assert(pa == pb);
- return pa;
-}
-
-/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
- * at most count entries. */
-void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
- if (count == 0)
- return;
-
- vBlocks.reserve(vBlocks.size() + count);
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- // Make sure pindexBestKnownBlock is up to date, we'll need it.
- ProcessBlockAvailability(nodeid);
-
- if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
- // This peer has nothing interesting.
- return;
- }
-
- if (state->pindexLastCommonBlock == NULL) {
- // Bootstrap quickly by guessing a parent of our best tip is the forking point.
- // Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
- }
-
- // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
- // of its current tip anymore. Go back enough to fix that.
- state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
- if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
- return;
-
- std::vector<CBlockIndex*> vToFetch;
- CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
- // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
- // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
- // download that next block if the window were 1 larger.
- int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
- int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
- NodeId waitingfor = -1;
- while (pindexWalk->nHeight < nMaxHeight) {
- // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
- // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
- // as iterating over ~100 CBlockIndex* entries anyway.
- int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
- vToFetch.resize(nToFetch);
- pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
- vToFetch[nToFetch - 1] = pindexWalk;
- for (unsigned int i = nToFetch - 1; i > 0; i--) {
- vToFetch[i - 1] = vToFetch[i]->pprev;
- }
-
- // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
- // are not yet downloaded and not in flight to vBlocks. In the mean time, update
- // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
- // already part of our chain (and therefore don't need it even if pruned).
- BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
- if (!pindex->IsValid(BLOCK_VALID_TREE)) {
- // We consider the chain that this peer is on invalid.
- return;
- }
- if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
- // We wouldn't download this block or its descendants from this peer.
- return;
- }
- if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
- if (pindex->nChainTx)
- state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
- // The block is not already downloaded, and not yet in flight.
- if (pindex->nHeight > nWindowEnd) {
- // We reached the end of the window.
- if (vBlocks.size() == 0 && waitingfor != nodeid) {
- // We aren't able to fetch anything, but we would be if the download window was one larger.
- nodeStaller = waitingfor;
- }
- return;
- }
- vBlocks.push_back(pindex);
- if (vBlocks.size() == count) {
- return;
- }
- } else if (waitingfor == -1) {
- // This is the first already-in-flight block.
- waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
- }
- }
- }
-}
-
-} // anon namespace
-
-bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
- if (state == NULL)
- return false;
- stats.nMisbehavior = state->nMisbehavior;
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
- BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) {
- if (queue.pindex)
- stats.vHeightInFlight.push_back(queue.pindex->nHeight);
- }
- return true;
-}
-
-void RegisterNodeSignals(CNodeSignals& nodeSignals)
-{
- nodeSignals.ProcessMessages.connect(&ProcessMessages);
- nodeSignals.SendMessages.connect(&SendMessages);
- nodeSignals.InitializeNode.connect(&InitializeNode);
- nodeSignals.FinalizeNode.connect(&FinalizeNode);
-}
-
-void UnregisterNodeSignals(CNodeSignals& nodeSignals)
-{
- nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
- nodeSignals.SendMessages.disconnect(&SendMessages);
- nodeSignals.InitializeNode.disconnect(&InitializeNode);
- nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
-}
-
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
@@ -729,112 +186,6 @@ enum FlushStateMode {
// See definition for documentation
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode);
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapOrphanTransactions
-//
-
-bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- uint256 hash = tx.GetHash();
- if (mapOrphanTransactions.count(hash))
- return false;
-
- // Ignore big transactions, to avoid a
- // send-big-orphans memory exhaustion attack. If a peer has a legitimate
- // large transaction with a missing parent then we assume
- // it will rebroadcast it later, after the parent transaction(s)
- // have been mined or received.
- // 100 orphans, each of which is at most 99,999 bytes big is
- // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
- unsigned int sz = GetTransactionWeight(tx);
- if (sz >= MAX_STANDARD_TX_WEIGHT)
- {
- LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
- return false;
- }
-
- auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
- assert(ret.second);
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
- }
-
- LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
- mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
- return true;
-}
-
-int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
- if (it == mapOrphanTransactions.end())
- return 0;
- BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
- {
- auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itPrev == mapOrphanTransactionsByPrev.end())
- continue;
- itPrev->second.erase(it);
- if (itPrev->second.empty())
- mapOrphanTransactionsByPrev.erase(itPrev);
- }
- mapOrphanTransactions.erase(it);
- return 1;
-}
-
-void EraseOrphansFor(NodeId peer)
-{
- int nErased = 0;
- map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
- }
- }
- if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
-}
-
-
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- unsigned int nEvicted = 0;
- static int64_t nNextSweep;
- int64_t nNow = GetTime();
- if (nNextSweep <= nNow) {
- // Sweep out expired orphan pool entries:
- int nErased = 0;
- int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
- map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- map<uint256, COrphanTx>::iterator maybeErase = iter++;
- if (maybeErase->second.nTimeExpire <= nNow) {
- nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
- } else {
- nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
- }
- }
- // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
- nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
- if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased);
- }
- while (mapOrphanTransactions.size() > nMaxOrphans)
- {
- // Evict a random orphan:
- uint256 randomhash = GetRandHash();
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- EraseOrphanTx(it->first);
- ++nEvicted;
- }
- return nEvicted;
-}
-
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
if (tx.nLockTime == 0)
@@ -1633,7 +984,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
}
/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
-bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
+bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
@@ -1642,7 +993,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
CTransactionRef ptx = mempool.get(hash);
if (ptx)
{
- txOut = *ptx;
+ txOut = ptx;
return true;
}
@@ -1661,7 +1012,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
}
hashBlock = header.GetHash();
- if (txOut.GetHash() != hash)
+ if (txOut->GetHash() != hash)
return error("%s: txid mismatch", __func__);
return true;
}
@@ -1684,7 +1035,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
for (const auto& tx : block.vtx) {
if (tx->GetHash() == hash) {
- txOut = *tx;
+ txOut = tx;
hashBlock = pindexSlow->GetBlockHash();
return true;
}
@@ -1893,26 +1244,6 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
CheckForkWarningConditions();
}
-// Requires cs_main.
-void Misbehaving(NodeId pnode, int howmuch)
-{
- if (howmuch == 0)
- return;
-
- CNodeState *state = State(pnode);
- if (state == NULL)
- return;
-
- state->nMisbehavior += howmuch;
- int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
- if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
- {
- LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
- state->fShouldBan = true;
- } else
- LogPrintf("%s: %s peer=%d (%d -> %d)\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
-}
-
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
@@ -2822,28 +2153,44 @@ static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
/**
+ * Used to track conflicted transactions removed from mempool and transactions
+ * applied to the UTXO state as a part of a single ActivateBestChainStep call.
+ */
+struct ConnectTrace {
+ std::vector<CTransactionRef> txConflicted;
+ std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
+};
+
+/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
+ *
+ * The block is always added to connectTrace (either after loading from disk or by copying
+ * pblock) - if that is not intended, care must be taken to remove the last entry in
+ * blocksConnected in case of failure.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::vector<CTransactionRef> &txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> &txChanged)
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
- CBlock block;
if (!pblock) {
- if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus()))
+ std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
+ connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
+ if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
- pblock = &block;
+ } else {
+ connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
}
+ const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
- bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams);
- GetMainSignals().BlockChecked(*pblock, state);
+ bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
+ GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
@@ -2861,13 +2208,10 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, &txConflicted, !IsInitialBlockDownload());
+ mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, &connectTrace.txConflicted, !IsInitialBlockDownload());
// Update chainActive & related variables.
UpdateTip(pindexNew, chainparams);
- for (unsigned int i=0; i < pblock->vtx.size(); i++)
- txChanged.emplace_back(pblock->vtx[i], pindexNew, i);
-
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
@@ -2948,7 +2292,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::vector<CTransactionRef>& txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>>& txChanged)
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -2981,7 +2325,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
- if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, txConflicted, txChanged)) {
+ if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (!state.CorruptionPossible())
@@ -2989,6 +2333,8 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
state = CValidationState();
fInvalidFound = true;
fContinue = false;
+ // If we didn't actually connect the block, don't notify listeners about it
+ connectTrace.blocksConnected.pop_back();
break;
} else {
// A system error occurred (disk space, database error, ...).
@@ -3046,20 +2392,16 @@ static void NotifyHeaderTip() {
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
- std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> txChanged;
- if (pblock)
- txChanged.reserve(pblock->vtx.size());
do {
- txChanged.clear();
boost::this_thread::interruption_point();
if (ShutdownRequested())
break;
const CBlockIndex *pindexFork;
- std::vector<CTransactionRef> txConflicted;
+ ConnectTrace connectTrace;
bool fInitialDownload;
{
LOCK(cs_main);
@@ -3073,7 +2415,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return true;
bool fInvalidFound = false;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound, txConflicted, txChanged))
+ std::shared_ptr<const CBlock> nullBlockPtr;
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
return false;
if (fInvalidFound) {
@@ -3090,13 +2433,17 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
// throw all transactions though the signal-interface
// while _not_ holding the cs_main lock
- for (const auto& tx : txConflicted)
+ for (const auto& tx : connectTrace.txConflicted)
{
GetMainSignals().SyncTransaction(*tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
}
// ... and about transactions that got confirmed:
- for (unsigned int i = 0; i < txChanged.size(); i++)
- GetMainSignals().SyncTransaction(*std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i]));
+ for (const auto& pair : connectTrace.blocksConnected) {
+ assert(pair.second);
+ const CBlock& block = *(pair.second);
+ for (unsigned int i = 0; i < block.vtx.size(); i++)
+ GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
+ }
// Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
@@ -3528,8 +2875,9 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
out.scriptPubKey[5] = 0xed;
memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
- const_cast<std::vector<CTxOut>*>(&block.vtx[0]->vout)->push_back(out);
- block.vtx[0]->UpdateHash();
+ CMutableTransaction tx(*block.vtx[0]);
+ tx.vout.push_back(out);
+ block.vtx[0] = MakeTransactionRef(std::move(tx));
}
}
UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
@@ -3780,7 +3128,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
return true;
}
-bool ProcessNewBlock(const CChainParams& chainparams, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool *fNewBlock)
+bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool *fNewBlock)
{
{
LOCK(cs_main);
@@ -4311,8 +3659,6 @@ void UnloadBlockIndex()
pindexBestInvalid = NULL;
pindexBestHeader = NULL;
mempool.clear();
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
nLastBlockFile = 0;
@@ -4718,2314 +4064,6 @@ std::string GetWarnings(const std::string& strFor)
assert(!"GetWarnings(): invalid parameter");
return "error";
}
-
-
-
-
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// blockchain -> download logic notification
-//
-
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
- // Initialize global variables that cannot be constructed at startup.
- recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
-}
-
-void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
- if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
- return;
-
- LOCK(cs_main);
-
- std::vector<uint256> vOrphanErase;
- // Which orphan pool entries must we evict?
- for (size_t j = 0; j < tx.vin.size(); j++) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = (*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
- }
- }
-
- // Erase orphan transactions include or precluded by this block
- if (vOrphanErase.size()) {
- int nErased = 0;
- BOOST_FOREACH(uint256 &orphanHash, vOrphanErase) {
- nErased += EraseOrphanTx(orphanHash);
- }
- LogPrint("mempool", "Erased %d orphan tx included or conflicted by block\n", nErased);
- }
-}
-
-void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
- const int nNewHeight = pindexNew->nHeight;
- connman->SetBestHeight(nNewHeight);
-
- if (!fInitialDownload) {
- // Find the hashes of all blocks that weren't previously in the best chain.
- std::vector<uint256> vHashes;
- const CBlockIndex *pindexToAnnounce = pindexNew;
- while (pindexToAnnounce != pindexFork) {
- vHashes.push_back(pindexToAnnounce->GetBlockHash());
- pindexToAnnounce = pindexToAnnounce->pprev;
- if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
- // Limit announcements in case of a huge reorganization.
- // Rely on the peer's synchronization mechanism in that case.
- break;
- }
- }
- // Relay inventory, but don't relay old inventory during initial block download.
- connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
- if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
- BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
- pnode->PushBlockHash(hash);
- }
- }
- });
- }
-
- nTimeBestReceived = GetTime();
-}
-
-void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
- LOCK(cs_main);
-
- const uint256 hash(block.GetHash());
- std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
-
- int nDoS = 0;
- if (state.IsInvalid(nDoS)) {
- if (it != mapBlockSource.end() && State(it->second.first)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
- CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
- State(it->second.first)->rejects.push_back(reject);
- if (nDoS > 0 && it->second.second)
- Misbehaving(it->second.first, nDoS);
- }
- }
- if (it != mapBlockSource.end())
- mapBlockSource.erase(it);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Messages
-//
-
-
-bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- switch (inv.type)
- {
- case MSG_TX:
- case MSG_WITNESS_TX:
- {
- assert(recentRejects);
- if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
- {
- // If the chain tip has changed previously rejected transactions
- // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
- // or a double-spend. Reset the rejects filter and give those
- // txs a second chance.
- hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
- recentRejects->reset();
- }
-
- // Use pcoinsTip->HaveCoinsInCache as a quick approximation to exclude
- // requesting or processing some txs which have already been included in a block
- return recentRejects->contains(inv.hash) ||
- mempool.exists(inv.hash) ||
- mapOrphanTransactions.count(inv.hash) ||
- pcoinsTip->HaveCoinsInCache(inv.hash);
- }
- case MSG_BLOCK:
- case MSG_WITNESS_BLOCK:
- return mapBlockIndex.count(inv.hash);
- }
- // Don't know what it is, just say we already got one
- return true;
-}
-
-static void RelayTransaction(const CTransaction& tx, CConnman& connman)
-{
- CInv inv(MSG_TX, tx.GetHash());
- connman.ForEachNode([&inv](CNode* pnode)
- {
- pnode->PushInventory(inv);
- });
-}
-
-static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
-{
- unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
-
- // Relay to a limited number of other nodes
- // Use deterministic randomness to send to the same nodes for 24 hours
- // at a time so the addrKnowns of the chosen nodes prevent repeats
- uint64_t hashAddr = addr.GetHash();
- const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
- FastRandomContext insecure_rand;
-
- std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}};
- assert(nRelayNodes <= best.size());
-
- auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->nVersion >= CADDR_TIME_VERSION) {
- uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
- for (unsigned int i = 0; i < nRelayNodes; i++) {
- if (hashKey > best[i].first) {
- std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);
- best[i] = std::make_pair(hashKey, pnode);
- break;
- }
- }
- }
- };
-
- auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] {
- for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
- best[i].second->PushAddress(addr, insecure_rand);
- }
- };
-
- connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
-}
-
-void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman)
-{
- std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
- vector<CInv> vNotFound;
- CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- LOCK(cs_main);
-
- while (it != pfrom->vRecvGetData.end()) {
- // Don't bother if send buffer is too full to respond anyway
- if (pfrom->nSendSize >= nMaxSendBufferSize)
- break;
-
- const CInv &inv = *it;
- {
- boost::this_thread::interruption_point();
- it++;
-
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
- {
- bool send = false;
- BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
- if (mi != mapBlockIndex.end())
- {
- if (chainActive.Contains(mi->second)) {
- send = true;
- } else {
- static const int nOneMonth = 30 * 24 * 60 * 60;
- // To prevent fingerprinting attacks, only send blocks outside of the active
- // chain if they are valid, and no more than a month older (both in time, and in
- // best equivalent proof of work) than the best header chain we know about.
- send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
- (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
- if (!send) {
- LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
- }
- }
- }
- // disconnect node in case we have reached the outbound limit for serving historical blocks
- // never disconnect whitelisted nodes
- static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
- if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
-
- //disconnect node
- pfrom->fDisconnect = true;
- send = false;
- }
- // Pruned nodes may have deleted the block, so check whether
- // it's available before trying to send.
- if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
- {
- // Send block from disk
- CBlock block;
- if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
- assert(!"cannot load block from disk");
- if (inv.type == MSG_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block));
- else if (inv.type == MSG_WITNESS_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block));
- else if (inv.type == MSG_FILTERED_BLOCK)
- {
- bool sendMerkleBlock = false;
- CMerkleBlock merkleBlock;
- {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
- sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
- }
- }
- if (sendMerkleBlock) {
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
- // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
- // This avoids hurting performance by pointlessly requiring a round-trip
- // Note that there is currently no way for a node to request any single transactions we didn't send here -
- // they must either disconnect and retry or request the full block.
- // Thus, the protocol spec specified allows for us to provide duplicate txn here,
- // however we MUST always provide at least what the remote peer needs
- typedef std::pair<unsigned int, uint256> PairType;
- BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]));
- }
- // else
- // no response
- }
- else if (inv.type == MSG_CMPCT_BLOCK)
- {
- // If a peer is asking for old blocks, we're almost guaranteed
- // they won't have a useful mempool to match against a compact block,
- // and we don't feel like constructing the object for them, so
- // instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
- int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
- CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- } else
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block));
- }
-
- // Trigger the peer node to send a getblocks request for the next batch of inventory
- if (inv.hash == pfrom->hashContinue)
- {
- // Bypass PushInventory, this must send even if redundant,
- // and we want it right after the last block so they don't
- // wait for other stuff first.
- vector<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
- pfrom->hashContinue.SetNull();
- }
- }
- }
- else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
- {
- // Send stream from relay memory
- bool push = false;
- auto mi = mapRelay.find(inv.hash);
- int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
- if (mi != mapRelay.end()) {
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
- push = true;
- } else if (pfrom->timeLastMempoolReq) {
- auto txinfo = mempool.info(inv.hash);
- // To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request.
- if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
- push = true;
- }
- }
- if (!push) {
- vNotFound.push_back(inv);
- }
- }
-
- // Track requests for our stuff.
- GetMainSignals().Inventory(inv.hash);
-
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
- break;
- }
- }
-
- pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
-
- if (!vNotFound.empty()) {
- // Let the peer know that we didn't find what it asked for, so it doesn't
- // have to wait around forever. Currently only SPV clients actually care
- // about this message: it's needed when they are recursively walking the
- // dependencies of relevant unconfirmed transactions. SPV clients want to
- // do that because they want to know about (and store and rebroadcast and
- // risk analyze) the dependencies of transactions relevant to them, without
- // having to download the entire memory pool.
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
- }
-}
-
-uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) {
- uint32_t nFetchFlags = 0;
- if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
- nFetchFlags |= MSG_WITNESS_FLAG;
- }
- return nFetchFlags;
-}
-
-bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman)
-{
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
-
- LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
- if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
- {
- LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
- return true;
- }
-
-
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
- (strCommand == NetMsgType::FILTERLOAD ||
- strCommand == NetMsgType::FILTERADD))
- {
- if (pfrom->nVersion >= NO_BLOOM_VERSION) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- return false;
- } else {
- pfrom->fDisconnect = true;
- return false;
- }
- }
-
-
- if (strCommand == NetMsgType::VERSION)
- {
- // Each connection can only send one version message
- if (pfrom->nVersion != 0)
- {
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")));
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
- }
-
- int64_t nTime;
- CAddress addrMe;
- CAddress addrFrom;
- uint64_t nNonce = 1;
- uint64_t nServiceInt;
- vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
- pfrom->nServices = ServiceFlags(nServiceInt);
- if (!pfrom->fInbound)
- {
- connman.SetServices(pfrom->addr, pfrom->nServices);
- }
- if (pfrom->nServicesExpected & ~pfrom->nServices)
- {
- LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
- strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
- pfrom->fDisconnect = true;
- return false;
- }
-
- if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
- {
- // disconnect from peers older than this proto version
- LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
- strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
- pfrom->fDisconnect = true;
- return false;
- }
-
- if (pfrom->nVersion == 10300)
- pfrom->nVersion = 300;
- if (!vRecv.empty())
- vRecv >> addrFrom >> nNonce;
- if (!vRecv.empty()) {
- vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
- pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
- }
- if (!vRecv.empty()) {
- vRecv >> pfrom->nStartingHeight;
- }
- {
- LOCK(pfrom->cs_filter);
- if (!vRecv.empty())
- vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
- else
- pfrom->fRelayTxes = true;
- }
-
- // Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
- {
- LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
- pfrom->fDisconnect = true;
- return true;
- }
-
- pfrom->addrLocal = addrMe;
- if (pfrom->fInbound && addrMe.IsRoutable())
- {
- SeenLocal(addrMe);
- }
-
- // Be shy and don't send version until we hear
- if (pfrom->fInbound)
- PushNodeVersion(pfrom, connman, GetAdjustedTime());
-
- pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
-
- if((pfrom->nServices & NODE_WITNESS))
- {
- LOCK(cs_main);
- State(pfrom->GetId())->fHaveWitness = true;
- }
-
- // Potentially mark this peer as a preferred download peer.
- {
- LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
- }
-
- // Change version
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
- int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION);
- pfrom->SetSendVersion(nSendVersion);
-
- if (!pfrom->fInbound)
- {
- // Advertise our address
- if (fListen && !IsInitialBlockDownload())
- {
- CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
- FastRandomContext insecure_rand;
- if (addr.IsRoutable())
- {
- LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
- } else if (IsPeerAddrLocalGood(pfrom)) {
- addr.SetIP(pfrom->addrLocal);
- LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
- }
- }
-
- // Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
- {
- connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
- pfrom->fGetAddr = true;
- }
- connman.MarkAddressGood(pfrom->addr);
- }
-
- pfrom->fSuccessfullyConnected = true;
-
- string remoteAddr;
- if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
-
- LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
- pfrom->cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
- remoteAddr);
-
- int64_t nTimeOffset = nTime - GetTime();
- pfrom->nTimeOffset = nTimeOffset;
- AddTimeData(pfrom->addr, nTimeOffset);
-
- // Feeler connections exist only to verify if address is online.
- if (pfrom->fFeeler) {
- assert(pfrom->fInbound == false);
- pfrom->fDisconnect = true;
- }
- return true;
- }
-
-
- else if (pfrom->nVersion == 0)
- {
- // Must have a version message before anything else
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
- }
-
- // At this point, the outgoing message serialization version can't change.
- CNetMsgMaker msgMaker(pfrom->GetSendVersion());
-
- if (strCommand == NetMsgType::VERACK)
- {
- pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
-
- if (!pfrom->fInbound) {
- // Mark this node as currently connected, so we update its timestamp later.
- LOCK(cs_main);
- State(pfrom->GetId())->fCurrentlyConnected = true;
- }
-
- if (pfrom->nVersion >= SENDHEADERS_VERSION) {
- // Tell our peer we prefer to receive headers rather than inv's
- // We send this to non-NODE NETWORK peers as well, because even
- // non-NODE NETWORK peers can announce blocks (such as pruning
- // nodes)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
- }
- if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
- // Tell our peer we are willing to provide version 1 or 2 cmpctblocks
- // However, we do not request new block announcements using
- // cmpctblock messages.
- // We send this to non-NODE NETWORK peers as well, because
- // they may wish to request compact blocks from us
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
- nCMPCTBLOCKVersion = 1;
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
- }
- }
-
-
- else if (strCommand == NetMsgType::ADDR)
- {
- vector<CAddress> vAddr;
- vRecv >> vAddr;
-
- // Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
- return true;
- if (vAddr.size() > 1000)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message addr size() = %u", vAddr.size());
- }
-
- // Store the new addresses
- vector<CAddress> vAddrOk;
- int64_t nNow = GetAdjustedTime();
- int64_t nSince = nNow - 10 * 60;
- BOOST_FOREACH(CAddress& addr, vAddr)
- {
- boost::this_thread::interruption_point();
-
- if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
- continue;
-
- if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
- addr.nTime = nNow - 5 * 24 * 60 * 60;
- pfrom->AddAddressKnown(addr);
- bool fReachable = IsReachable(addr);
- if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
- {
- // Relay to a limited number of other nodes
- RelayAddress(addr, fReachable, connman);
- }
- // Do not store addresses outside our network
- if (fReachable)
- vAddrOk.push_back(addr);
- }
- connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
- if (vAddr.size() < 1000)
- pfrom->fGetAddr = false;
- if (pfrom->fOneShot)
- pfrom->fDisconnect = true;
- }
-
- else if (strCommand == NetMsgType::SENDHEADERS)
- {
- LOCK(cs_main);
- State(pfrom->GetId())->fPreferHeaders = true;
- }
-
- else if (strCommand == NetMsgType::SENDCMPCT)
- {
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 0;
- vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
- LOCK(cs_main);
- // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
- if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
- State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
- }
- if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
- State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
- else
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
- }
- }
- }
-
-
- else if (strCommand == NetMsgType::INV)
- {
- vector<CInv> vInv;
- vRecv >> vInv;
- if (vInv.size() > MAX_INV_SZ)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message inv size() = %u", vInv.size());
- }
-
- bool fBlocksOnly = !fRelayTxes;
-
- // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
- fBlocksOnly = false;
-
- LOCK(cs_main);
-
- uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
-
- std::vector<CInv> vToFetch;
-
- for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
- {
- CInv &inv = vInv[nInv];
-
- boost::this_thread::interruption_point();
-
- bool fAlreadyHave = AlreadyHave(inv);
- LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
-
- if (inv.type == MSG_TX) {
- inv.type |= nFetchFlags;
- }
-
- if (inv.type == MSG_BLOCK) {
- UpdateBlockAvailability(pfrom->GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
- // We used to request the full block here, but since headers-announcements are now the
- // primary method of announcement on the network, and since, in the case that a node
- // fell back to inv we probably have a reorg which we should get the headers for first,
- // we now only provide a getheaders response here. When we receive the headers, we will
- // then ask for the blocks we need.
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
- LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
- }
- }
- else
- {
- pfrom->AddInventoryKnown(inv);
- if (fBlocksOnly)
- LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
- else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload())
- pfrom->AskFor(inv);
- }
-
- // Track requests for our stuff
- GetMainSignals().Inventory(inv.hash);
-
- if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) {
- Misbehaving(pfrom->GetId(), 50);
- return error("send buffer size() = %u", pfrom->nSendSize);
- }
- }
-
- if (!vToFetch.empty())
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vToFetch));
- }
-
-
- else if (strCommand == NetMsgType::GETDATA)
- {
- vector<CInv> vInv;
- vRecv >> vInv;
- if (vInv.size() > MAX_INV_SZ)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message getdata size() = %u", vInv.size());
- }
-
- if (fDebug || (vInv.size() != 1))
- LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
-
- if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
- LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
-
- pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
- }
-
-
- else if (strCommand == NetMsgType::GETBLOCKS)
- {
- CBlockLocator locator;
- uint256 hashStop;
- vRecv >> locator >> hashStop;
-
- LOCK(cs_main);
-
- // Find the last block the caller has in the main chain
- CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
-
- // Send the rest of the chain
- if (pindex)
- pindex = chainActive.Next(pindex);
- int nLimit = 500;
- LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
- for (; pindex; pindex = chainActive.Next(pindex))
- {
- if (pindex->GetBlockHash() == hashStop)
- {
- LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- break;
- }
- // If pruning, don't inv blocks unless we have on disk and are likely to still have
- // for some reasonable time window (1 hour) that block relay might require.
- const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
- {
- LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- break;
- }
- pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
- if (--nLimit <= 0)
- {
- // When this block is requested, we'll send an inv that'll
- // trigger the peer to getblocks the next batch of inventory.
- LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- pfrom->hashContinue = pindex->GetBlockHash();
- break;
- }
- }
- }
-
-
- else if (strCommand == NetMsgType::GETBLOCKTXN)
- {
- BlockTransactionsRequest req;
- vRecv >> req;
-
- LOCK(cs_main);
-
- BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
- if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
- LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
- return true;
- }
-
- if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
- // If an older block is requested (should never happen in practice,
- // but can happen in tests) send a block response instead of a
- // blocktxn response. Sending a full block response instead of a
- // small blocktxn response is preferable in the case where a peer
- // might maliciously send lots of getblocktxn requests to trigger
- // expensive disk reads, because it will require the peer to
- // actually receive all the data read from disk over the network.
- LogPrint("net", "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH);
- CInv inv;
- inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
- inv.hash = req.blockhash;
- pfrom->vRecvGetData.push_back(inv);
- ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
- return true;
- }
-
- CBlock block;
- assert(ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()));
-
- BlockTransactions resp(req);
- for (size_t i = 0; i < req.indexes.size(); i++) {
- if (req.indexes[i] >= block.vtx.size()) {
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id);
- return true;
- }
- resp.txn[i] = block.vtx[req.indexes[i]];
- }
- int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
- }
-
-
- else if (strCommand == NetMsgType::GETHEADERS)
- {
- CBlockLocator locator;
- uint256 hashStop;
- vRecv >> locator >> hashStop;
-
- LOCK(cs_main);
- if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
- LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
- return true;
- }
-
- CNodeState *nodestate = State(pfrom->GetId());
- CBlockIndex* pindex = NULL;
- if (locator.IsNull())
- {
- // If locator is null, return the hashStop block
- BlockMap::iterator mi = mapBlockIndex.find(hashStop);
- if (mi == mapBlockIndex.end())
- return true;
- pindex = (*mi).second;
- }
- else
- {
- // Find the last block the caller has in the main chain
- pindex = FindForkInGlobalIndex(chainActive, locator);
- if (pindex)
- pindex = chainActive.Next(pindex);
- }
-
- // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
- vector<CBlock> vHeaders;
- int nLimit = MAX_HEADERS_RESULTS;
- LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
- for (; pindex; pindex = chainActive.Next(pindex))
- {
- vHeaders.push_back(pindex->GetBlockHeader());
- if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
- break;
- }
- // pindex can be NULL either if we sent chainActive.Tip() OR
- // if our peer has chainActive.Tip() (and thus we are sending an empty
- // headers message). In both cases it's safe to update
- // pindexBestHeaderSent to be our tip.
- nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
- }
-
-
- else if (strCommand == NetMsgType::TX)
- {
- // Stop processing the transaction early if
- // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
- {
- LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
- return true;
- }
-
- deque<COutPoint> vWorkQueue;
- vector<uint256> vEraseQueue;
- CTransaction tx;
- vRecv >> tx;
-
- CInv inv(MSG_TX, tx.GetHash());
- pfrom->AddInventoryKnown(inv);
-
- LOCK(cs_main);
-
- bool fMissingInputs = false;
- CValidationState state;
-
- pfrom->setAskFor.erase(inv.hash);
- mapAlreadyAskedFor.erase(inv.hash);
-
- if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) {
- mempool.check(pcoinsTip);
- RelayTransaction(tx, connman);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- vWorkQueue.emplace_back(inv.hash, i);
- }
-
- pfrom->nLastTXTime = GetTime();
-
- LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->id,
- tx.GetHash().ToString(),
- mempool.size(), mempool.DynamicMemoryUsage() / 1000);
-
- // Recursively process any orphan transactions that depended on this one
- set<NodeId> setMisbehaving;
- while (!vWorkQueue.empty()) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
- vWorkQueue.pop_front();
- if (itByPrev == mapOrphanTransactionsByPrev.end())
- continue;
- for (auto mi = itByPrev->second.begin();
- mi != itByPrev->second.end();
- ++mi)
- {
- const CTransaction& orphanTx = (*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- NodeId fromPeer = (*mi)->second.fromPeer;
- bool fMissingInputs2 = false;
- // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
- // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
- // anyone relaying LegitTxX banned)
- CValidationState stateDummy;
-
-
- if (setMisbehaving.count(fromPeer))
- continue;
- if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
- LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanTx, connman);
- for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
- vWorkQueue.emplace_back(orphanHash, i);
- }
- vEraseQueue.push_back(orphanHash);
- }
- else if (!fMissingInputs2)
- {
- int nDos = 0;
- if (stateDummy.IsInvalid(nDos) && nDos > 0)
- {
- // Punish peer that gave us an invalid orphan tx
- Misbehaving(fromPeer, nDos);
- setMisbehaving.insert(fromPeer);
- LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
- }
- // Has inputs but not accepted to mempool
- // Probably non-standard or insufficient fee/priority
- LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
- vEraseQueue.push_back(orphanHash);
- if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(orphanHash);
- }
- }
- mempool.check(pcoinsTip);
- }
- }
-
- BOOST_FOREACH(uint256 hash, vEraseQueue)
- EraseOrphanTx(hash);
- }
- else if (fMissingInputs)
- {
- bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- if (recentRejects->contains(txin.prevout.hash)) {
- fRejectedParents = true;
- break;
- }
- }
- if (!fRejectedParents) {
- uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
- pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv)) pfrom->AskFor(_inv);
- }
- AddOrphanTx(tx, pfrom->GetId());
-
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
- unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
- unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
- if (nEvicted > 0)
- LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
- } else {
- LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
- }
- } else {
- if (tx.wit.IsNull() && !state.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(tx.GetHash());
- }
-
- if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
- // Always relay transactions received from whitelisted peers, even
- // if they were already in the mempool or rejected from it due
- // to policy, allowing the node to function as a gateway for
- // nodes hidden behind it.
- //
- // Never relay transactions that we would assign a non-zero DoS
- // score for, as we expect peers to do the same with us in that
- // case.
- int nDoS = 0;
- if (!state.IsInvalid(nDoS) || nDoS == 0) {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
- RelayTransaction(tx, connman);
- } else {
- LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
- }
- }
- }
- int nDoS = 0;
- if (state.IsInvalid(nDoS))
- {
- LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
- pfrom->id,
- FormatStateMessage(state));
- if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
- state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
- if (nDoS > 0) {
- Misbehaving(pfrom->GetId(), nDoS);
- }
- }
- }
-
-
- else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- CBlockHeaderAndShortTxIDs cmpctblock;
- vRecv >> cmpctblock;
-
- {
- LOCK(cs_main);
-
- if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) {
- // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!IsInitialBlockDownload())
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
- return true;
- }
- }
-
- CBlockIndex *pindex = NULL;
- CValidationState state;
- if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- if (nDoS > 0) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS);
- }
- LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
- return true;
- }
- }
-
- LOCK(cs_main);
- // If AcceptBlockHeader returned true, it set pindex
- assert(pindex);
- UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
-
- std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
- bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
-
- if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
- return true;
-
- if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
- pindex->nTx != 0) { // We had this block at some point, but pruned it
- if (fAlreadyInFlight) {
- // We requested this block for some reason, but our mempool will probably be useless
- // so we just grab the block via normal getdata
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- }
- return true;
- }
-
- // If we're not close to tip yet, give up and let parallel block fetch work its magic
- if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
- return true;
-
- CNodeState *nodestate = State(pfrom->GetId());
-
- if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
- // Don't bother trying to process compact blocks from v1 peers
- // after segwit activates.
- return true;
- }
-
- // We want to be a bit conservative just to be extra careful about DoS
- // possibilities in compact block processing...
- if (pindex->nHeight <= chainActive.Height() + 2) {
- if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
- (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
- list<QueuedBlock>::iterator *queuedBlockIt = NULL;
- if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) {
- if (!(*queuedBlockIt)->partialBlock)
- (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
- else {
- // The block was already in flight using compact blocks from the same peer
- LogPrint("net", "Peer sent us compact block we were already syncing!\n");
- return true;
- }
- }
-
- PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
- ReadStatus status = partialBlock.InitData(cmpctblock);
- if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id);
- return true;
- } else if (status == READ_STATUS_FAILED) {
- // Duplicate txindexes, the block is now in-flight, so just request it
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- return true;
- }
-
- if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) {
- // We seem to be rather well-synced, so it appears pfrom was the first to provide us
- // with this block! Let's get them to announce using compact blocks in the future.
- MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
- }
-
- BlockTransactionsRequest req;
- for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
- if (!partialBlock.IsTxAvailable(i))
- req.indexes.push_back(i);
- }
- if (req.indexes.empty()) {
- // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
- BlockTransactions txn;
- txn.blockhash = cmpctblock.header.GetHash();
- CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
- blockTxnMsg << txn;
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman);
- } else {
- req.blockhash = pindex->GetBlockHash();
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
- }
- }
- } else {
- if (fAlreadyInFlight) {
- // We requested this block, but its far into the future, so our
- // mempool will probably be useless - request the block normally
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- return true;
- } else {
- // If this was an announce-cmpctblock, we want the same treatment as a header message
- // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
- std::vector<CBlock> headers;
- headers.push_back(cmpctblock.header);
- CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
- vHeadersMsg << headers;
- return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
- }
- }
- }
-
- else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- BlockTransactions resp;
- vRecv >> resp;
-
- CBlock block;
- bool fBlockRead = false;
- {
- LOCK(cs_main);
-
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
- if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
- it->second.first != pfrom->GetId()) {
- LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
- return true;
- }
-
- PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
- ReadStatus status = partialBlock.FillBlock(block, resp.txn);
- if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
- return true;
- } else if (status == READ_STATUS_FAILED) {
- // Might have collided, fall back to getdata now :(
- std::vector<CInv> invs;
- invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
- } else {
- // Block is either okay, or possibly we received
- // READ_STATUS_CHECKBLOCK_FAILED.
- // Note that CheckBlock can only fail for one of a few reasons:
- // 1. bad-proof-of-work (impossible here, because we've already
- // accepted the header)
- // 2. merkleroot doesn't match the transactions given (already
- // caught in FillBlock with READ_STATUS_FAILED, so
- // impossible here)
- // 3. the block is otherwise invalid (eg invalid coinbase,
- // block is too big, too many legacy sigops, etc).
- // So if CheckBlock failed, #3 is the only possibility.
- // Under BIP 152, we don't DoS-ban unless proof of work is
- // invalid (we don't require all the stateless checks to have
- // been run). This is handled below, so just treat this as
- // though the block was successfully read, and rely on the
- // handling in ProcessNewBlock to ensure the block index is
- // updated, reject messages go out, etc.
- MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
- fBlockRead = true;
- // mapBlockSource is only used for sending reject messages and DoS scores,
- // so the race between here and cs_main in ProcessNewBlock is fine.
- // BIP 152 permits peers to relay compact blocks after validating
- // the header only; we should not punish peers if the block turns
- // out to be invalid.
- mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
- }
- } // Don't hold cs_main when we call into ProcessNewBlock
- if (fBlockRead) {
- bool fNewBlock = false;
- // Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
- // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
- ProcessNewBlock(chainparams, &block, true, NULL, &fNewBlock);
- if (fNewBlock)
- pfrom->nLastBlockTime = GetTime();
- }
- }
-
-
- else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
- {
- std::vector<CBlockHeader> headers;
-
- // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
- unsigned int nCount = ReadCompactSize(vRecv);
- if (nCount > MAX_HEADERS_RESULTS) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("headers message size = %u", nCount);
- }
- headers.resize(nCount);
- for (unsigned int n = 0; n < nCount; n++) {
- vRecv >> headers[n];
- ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
- }
-
- if (nCount == 0) {
- // Nothing interesting. Stop asking this peers for more headers.
- return true;
- }
-
- CBlockIndex *pindexLast = NULL;
- {
- LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
-
- // If this looks like it could be a block announcement (nCount <
- // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
- // don't connect:
- // - Send a getheaders message in response to try to connect the chain.
- // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
- // don't connect before giving DoS points
- // - Once a headers message is received that is valid and does connect,
- // nUnconnectingHeaders gets reset back to 0.
- if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
- nodestate->nUnconnectingHeaders++;
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
- LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
- headers[0].GetHash().ToString(),
- headers[0].hashPrevBlock.ToString(),
- pindexBestHeader->nHeight,
- pfrom->id, nodestate->nUnconnectingHeaders);
- // Set hashLastUnknownBlock for this peer, so that if we
- // eventually get the headers - even from a different peer -
- // we can use this peer to download.
- UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
-
- if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom->GetId(), 20);
- }
- return true;
- }
-
- uint256 hashLastBlock;
- for (const CBlockHeader& header : headers) {
- if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
- Misbehaving(pfrom->GetId(), 20);
- return error("non-continuous headers sequence");
- }
- hashLastBlock = header.GetHash();
- }
- }
-
- CValidationState state;
- if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- if (nDoS > 0) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS);
- }
- return error("invalid header received");
- }
- }
-
- {
- LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
- if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
- }
- nodestate->nUnconnectingHeaders = 0;
-
- assert(pindexLast);
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
-
- if (nCount == MAX_HEADERS_RESULTS) {
- // Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
- // from there instead.
- LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
- }
-
- bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
- // If this set of headers is valid and ends in a block with at least as
- // much work as our tip, download as much as possible.
- if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
- vector<CBlockIndex *> vToFetch;
- CBlockIndex *pindexWalk = pindexLast;
- // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
- // We don't have this block, and it's not yet in flight.
- vToFetch.push_back(pindexWalk);
- }
- pindexWalk = pindexWalk->pprev;
- }
- // If pindexWalk still isn't on our main chain, we're looking at a
- // very large reorg at a time we think we're close to caught up to
- // the main chain -- this shouldn't really happen. Bail out on the
- // direct fetch and rely on parallel download instead.
- if (!chainActive.Contains(pindexWalk)) {
- LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
- pindexLast->GetBlockHash().ToString(),
- pindexLast->nHeight);
- } else {
- vector<CInv> vGetData;
- // Download as much as possible, from earliest to latest.
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
- if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- // Can't download any more from this peer
- break;
- }
- uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus());
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
- LogPrint("net", "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->id);
- }
- if (vGetData.size() > 1) {
- LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
- pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
- }
- if (vGetData.size() > 0) {
- if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
- // We seem to be rather well-synced, so it appears pfrom was the first to provide us
- // with this block! Let's get them to announce using compact blocks in the future.
- MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
- // In any case, we want to download using a compact block, not a regular one
- vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
- }
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- }
- }
- }
- }
- }
-
- else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- CBlock block;
- vRecv >> block;
-
- LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id);
-
- // Process all blocks from whitelisted peers, even if not requested,
- // unless we're still syncing with the network.
- // Such an unrequested block may still be processed, subject to the
- // conditions in AcceptBlock().
- bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
- const uint256 hash(block.GetHash());
- {
- LOCK(cs_main);
- // Also always process if we requested the block explicitly, as we may
- // need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived(hash);
- // mapBlockSource is only used for sending reject messages and DoS scores,
- // so the race between here and cs_main in ProcessNewBlock is fine.
- mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
- }
- bool fNewBlock = false;
- ProcessNewBlock(chainparams, &block, forceProcessing, NULL, &fNewBlock);
- if (fNewBlock)
- pfrom->nLastBlockTime = GetTime();
- }
-
-
- else if (strCommand == NetMsgType::GETADDR)
- {
- // This asymmetric behavior for inbound and outbound connections was introduced
- // to prevent a fingerprinting attack: an attacker can send specific fake addresses
- // to users' AddrMan and later request them by sending getaddr messages.
- // Making nodes which are behind NAT and can only make outgoing connections ignore
- // the getaddr message mitigates the attack.
- if (!pfrom->fInbound) {
- LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id);
- return true;
- }
-
- // Only send one GetAddr response per connection to reduce resource waste
- // and discourage addr stamping of INV announcements.
- if (pfrom->fSentAddr) {
- LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
- return true;
- }
- pfrom->fSentAddr = true;
-
- pfrom->vAddrToSend.clear();
- vector<CAddress> vAddr = connman.GetAddresses();
- FastRandomContext insecure_rand;
- BOOST_FOREACH(const CAddress &addr, vAddr)
- pfrom->PushAddress(addr, insecure_rand);
- }
-
-
- else if (strCommand == NetMsgType::MEMPOOL)
- {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
- }
-
- if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
- }
-
- LOCK(pfrom->cs_inventory);
- pfrom->fSendMempool = true;
- }
-
-
- else if (strCommand == NetMsgType::PING)
- {
- if (pfrom->nVersion > BIP0031_VERSION)
- {
- uint64_t nonce = 0;
- vRecv >> nonce;
- // Echo the message back with the nonce. This allows for two useful features:
- //
- // 1) A remote node can quickly check if the connection is operational
- // 2) Remote nodes can measure the latency of the network thread. If this node
- // is overloaded it won't respond to pings quickly and the remote node can
- // avoid sending us more work, like chain download requests.
- //
- // The nonce stops the remote getting confused between different pings: without
- // it, if the remote node sends a ping once per second and this node takes 5
- // seconds to respond to each, the 5th ping the remote sends would appear to
- // return very quickly.
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
- }
- }
-
-
- else if (strCommand == NetMsgType::PONG)
- {
- int64_t pingUsecEnd = nTimeReceived;
- uint64_t nonce = 0;
- size_t nAvail = vRecv.in_avail();
- bool bPingFinished = false;
- std::string sProblem;
-
- if (nAvail >= sizeof(nonce)) {
- vRecv >> nonce;
-
- // Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom->nPingNonceSent != 0) {
- if (nonce == pfrom->nPingNonceSent) {
- // Matching pong received, this ping is no longer outstanding
- bPingFinished = true;
- int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
- if (pingUsecTime > 0) {
- // Successful ping time measurement, replace previous
- pfrom->nPingUsecTime = pingUsecTime;
- pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
- } else {
- // This should never happen
- sProblem = "Timing mishap";
- }
- } else {
- // Nonce mismatches are normal when pings are overlapping
- sProblem = "Nonce mismatch";
- if (nonce == 0) {
- // This is most likely a bug in another implementation somewhere; cancel this ping
- bPingFinished = true;
- sProblem = "Nonce zero";
- }
- }
- } else {
- sProblem = "Unsolicited pong without ping";
- }
- } else {
- // This is most likely a bug in another implementation somewhere; cancel this ping
- bPingFinished = true;
- sProblem = "Short payload";
- }
-
- if (!(sProblem.empty())) {
- LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->id,
- sProblem,
- pfrom->nPingNonceSent,
- nonce,
- nAvail);
- }
- if (bPingFinished) {
- pfrom->nPingNonceSent = 0;
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERLOAD)
- {
- CBloomFilter filter;
- vRecv >> filter;
-
- if (!filter.IsWithinSizeConstraints())
- {
- // There is no excuse for sending a too-large filter
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- }
- else
- {
- LOCK(pfrom->cs_filter);
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter(filter);
- pfrom->pfilter->UpdateEmptyFull();
- pfrom->fRelayTxes = true;
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERADD)
- {
- vector<unsigned char> vData;
- vRecv >> vData;
-
- // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
- // and thus, the maximum size any matched object can have) in a filteradd message
- bool bad = false;
- if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
- bad = true;
- } else {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
- pfrom->pfilter->insert(vData);
- } else {
- bad = true;
- }
- }
- if (bad) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERCLEAR)
- {
- LOCK(pfrom->cs_filter);
- if (pfrom->GetLocalServices() & NODE_BLOOM) {
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter();
- }
- pfrom->fRelayTxes = true;
- }
-
-
- else if (strCommand == NetMsgType::REJECT)
- {
- if (fDebug) {
- try {
- string strMsg; unsigned char ccode; string strReason;
- vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
-
- ostringstream ss;
- ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
-
- if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
- {
- uint256 hash;
- vRecv >> hash;
- ss << ": hash " << hash.ToString();
- }
- LogPrint("net", "Reject %s\n", SanitizeString(ss.str()));
- } catch (const std::ios_base::failure&) {
- // Avoid feedback loops by preventing reject messages from triggering a new reject message.
- LogPrint("net", "Unparseable reject message received\n");
- }
- }
- }
-
- else if (strCommand == NetMsgType::FEEFILTER) {
- CAmount newFeeFilter = 0;
- vRecv >> newFeeFilter;
- if (MoneyRange(newFeeFilter)) {
- {
- LOCK(pfrom->cs_feeFilter);
- pfrom->minFeeFilter = newFeeFilter;
- }
- LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
- }
- }
-
- else if (strCommand == NetMsgType::NOTFOUND) {
- // We do not care about the NOTFOUND message, but logging an Unknown Command
- // message would be undesirable as we transmit it ourselves.
- }
-
- else {
- // Ignore unknown commands for extensibility
- LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
- }
-
-
-
- return true;
-}
-
-// requires LOCK(cs_vRecvMsg)
-bool ProcessMessages(CNode* pfrom, CConnman& connman)
-{
- const CChainParams& chainparams = Params();
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
- //if (fDebug)
- // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size());
-
- //
- // Message format
- // (4) message start
- // (12) command
- // (4) size
- // (4) checksum
- // (x) data
- //
- bool fOk = true;
-
- if (!pfrom->vRecvGetData.empty())
- ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
-
- // this maintains the order of responses
- if (!pfrom->vRecvGetData.empty()) return fOk;
-
- std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin();
- while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) {
- // Don't bother if send buffer is too full to respond anyway
- if (pfrom->nSendSize >= nMaxSendBufferSize)
- break;
-
- // get next message
- CNetMessage& msg = *it;
-
- //if (fDebug)
- // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__,
- // msg.hdr.nMessageSize, msg.vRecv.size(),
- // msg.complete() ? "Y" : "N");
-
- // end, if an incomplete message is found
- if (!msg.complete())
- break;
-
- // at this point, any failure means we can delete the current message
- it++;
-
- // Scan for message start
- if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
- fOk = false;
- break;
- }
-
- // Read header
- CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(chainparams.MessageStart()))
- {
- LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
- continue;
- }
- string strCommand = hdr.GetCommand();
-
- // Message size
- unsigned int nMessageSize = hdr.nMessageSize;
-
- // Checksum
- CDataStream& vRecv = msg.vRecv;
- const uint256& hash = msg.GetMessageHash();
- if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
- {
- LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
- SanitizeString(strCommand), nMessageSize,
- HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
- HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
- continue;
- }
-
- // Process message
- bool fRet = false;
- try
- {
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman);
- boost::this_thread::interruption_point();
- }
- catch (const std::ios_base::failure& e)
- {
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")));
- if (strstr(e.what(), "end of data"))
- {
- // Allow exceptions from under-length message on vRecv
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
- // Allow exceptions from over-long size
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
- // Allow exceptions from non-canonical encoding
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
- PrintExceptionContinue(&e, "ProcessMessages()");
- }
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "ProcessMessages()");
- } catch (...) {
- PrintExceptionContinue(NULL, "ProcessMessages()");
- }
-
- if (!fRet)
- LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
-
- break;
- }
-
- // In case the connection got shut down, its receive buffer was wiped
- if (!pfrom->fDisconnect)
- pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
-
- return fOk;
-}
-
-class CompareInvMempoolOrder
-{
- CTxMemPool *mp;
-public:
- CompareInvMempoolOrder(CTxMemPool *_mempool)
- {
- mp = _mempool;
- }
-
- bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
- {
- /* As std::make_heap produces a max-heap, we want the entries with the
- * fewest ancestors/highest fee to sort later. */
- return mp->CompareDepthAndScore(*b, *a);
- }
-};
-
-bool SendMessages(CNode* pto, CConnman& connman)
-{
- const Consensus::Params& consensusParams = Params().GetConsensus();
- {
- // Don't send anything until we get its version message
- if (pto->nVersion == 0 || pto->fDisconnect)
- return true;
-
- // If we get here, the outgoing message serialization version is set and can't change.
- CNetMsgMaker msgMaker(pto->GetSendVersion());
-
- //
- // Message: ping
- //
- bool pingSend = false;
- if (pto->fPingQueued) {
- // RPC ping request by user
- pingSend = true;
- }
- if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
- // Ping automatically sent as a latency probe & keepalive.
- pingSend = true;
- }
- if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
- }
- pto->fPingQueued = false;
- pto->nPingUsecStart = GetTimeMicros();
- if (pto->nVersion > BIP0031_VERSION) {
- pto->nPingNonceSent = nonce;
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
- } else {
- // Peer is too old to support ping command with nonce, pong will never arrive.
- pto->nPingNonceSent = 0;
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
- }
- }
-
- TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
- if (!lockMain)
- return true;
-
- CNodeState &state = *State(pto->GetId());
-
- BOOST_FOREACH(const CBlockReject& reject, state.rejects)
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
- state.rejects.clear();
-
- if (state.fShouldBan) {
- state.fShouldBan = false;
- if (pto->fWhitelisted)
- LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
- else {
- pto->fDisconnect = true;
- if (pto->addr.IsLocal())
- LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
- else
- {
- connman.Ban(pto->addr, BanReasonNodeMisbehaving);
- }
- return true;
- }
- }
-
- // Address refresh broadcast
- int64_t nNow = GetTimeMicros();
- if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
- AdvertiseLocal(pto);
- pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
- }
-
- //
- // Message: addr
- //
- if (pto->nNextAddrSend < nNow) {
- pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
- vector<CAddress> vAddr;
- vAddr.reserve(pto->vAddrToSend.size());
- BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
- {
- if (!pto->addrKnown.contains(addr.GetKey()))
- {
- pto->addrKnown.insert(addr.GetKey());
- vAddr.push_back(addr);
- // receiver rejects addr messages larger than 1000
- if (vAddr.size() >= 1000)
- {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
- vAddr.clear();
- }
- }
- }
- pto->vAddrToSend.clear();
- if (!vAddr.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
- // we only send the big addr message once
- if (pto->vAddrToSend.capacity() > 40)
- pto->vAddrToSend.shrink_to_fit();
- }
-
- // Start block sync
- if (pindexBestHeader == NULL)
- pindexBestHeader = chainActive.Tip();
- bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
- if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
- // Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
- state.fSyncStarted = true;
- nSyncStarted++;
- const CBlockIndex *pindexStart = pindexBestHeader;
- /* If possible, start at the block preceding the currently
- best known header. This ensures that we always get a
- non-empty list of headers back as long as the peer
- is up-to-date. With a non-empty response, we can initialise
- the peer's known best block. This wouldn't be possible
- if we requested starting at pindexBestHeader and
- got back an empty response. */
- if (pindexStart->pprev)
- pindexStart = pindexStart->pprev;
- LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
- }
- }
-
- // Resend wallet transactions that haven't gotten in a block yet
- // Except during reindex, importing and IBD, when old wallet
- // transactions become unconfirmed and spams other nodes.
- if (!fReindex && !fImporting && !IsInitialBlockDownload())
- {
- GetMainSignals().Broadcast(nTimeBestReceived, &connman);
- }
-
- //
- // Try sending block announcements via headers
- //
- {
- // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
- // list of block hashes we're relaying, and our peer wants
- // headers announcements, then find the first header
- // not yet known to our peer but would connect, and send.
- // If no header would connect, or if we have too many
- // blocks, or if the peer doesn't want headers, just
- // add all to the inv queue.
- LOCK(pto->cs_inventory);
- vector<CBlock> vHeaders;
- bool fRevertToInv = ((!state.fPreferHeaders &&
- (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) ||
- pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
- CBlockIndex *pBestIndex = NULL; // last header queued for delivery
- ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
-
- if (!fRevertToInv) {
- bool fFoundStartingHeader = false;
- // Try to find first header that our peer doesn't have, and
- // then send all headers past that one. If we come across any
- // headers that aren't on chainActive, give up.
- BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
- BlockMap::iterator mi = mapBlockIndex.find(hash);
- assert(mi != mapBlockIndex.end());
- CBlockIndex *pindex = mi->second;
- if (chainActive[pindex->nHeight] != pindex) {
- // Bail out if we reorged away from this block
- fRevertToInv = true;
- break;
- }
- if (pBestIndex != NULL && pindex->pprev != pBestIndex) {
- // This means that the list of blocks to announce don't
- // connect to each other.
- // This shouldn't really be possible to hit during
- // regular operation (because reorgs should take us to
- // a chain that has some block not on the prior chain,
- // which should be caught by the prior check), but one
- // way this could happen is by using invalidateblock /
- // reconsiderblock repeatedly on the tip, causing it to
- // be added multiple times to vBlockHashesToAnnounce.
- // Robustly deal with this rare situation by reverting
- // to an inv.
- fRevertToInv = true;
- break;
- }
- pBestIndex = pindex;
- if (fFoundStartingHeader) {
- // add this to the headers message
- vHeaders.push_back(pindex->GetBlockHeader());
- } else if (PeerHasHeader(&state, pindex)) {
- continue; // keep looking for the first new block
- } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
- // Peer doesn't have this header but they do have the prior one.
- // Start sending headers.
- fFoundStartingHeader = true;
- vHeaders.push_back(pindex->GetBlockHeader());
- } else {
- // Peer doesn't have this header or the prior one -- nothing will
- // connect, so bail out.
- fRevertToInv = true;
- break;
- }
- }
- }
- if (!fRevertToInv && !vHeaders.empty()) {
- if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
- // We only send up to 1 block as header-and-ids, as otherwise
- // probably means we're doing an initial-ish-sync or they're slow
- LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
- //TODO: Shouldn't need to reload block from disk, but requires refactor
- CBlock block;
- assert(ReadBlockFromDisk(block, pBestIndex, consensusParams));
- CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
- int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- state.pindexBestHeaderSent = pBestIndex;
- } else if (state.fPreferHeaders) {
- if (vHeaders.size() > 1) {
- LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
- vHeaders.size(),
- vHeaders.front().GetHash().ToString(),
- vHeaders.back().GetHash().ToString(), pto->id);
- } else {
- LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
- }
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
- state.pindexBestHeaderSent = pBestIndex;
- } else
- fRevertToInv = true;
- }
- if (fRevertToInv) {
- // If falling back to using an inv, just try to inv the tip.
- // The last entry in vBlockHashesToAnnounce was our tip at some point
- // in the past.
- if (!pto->vBlockHashesToAnnounce.empty()) {
- const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
- BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
- assert(mi != mapBlockIndex.end());
- CBlockIndex *pindex = mi->second;
-
- // Warn if we're announcing a block that is not on the main chain.
- // This should be very rare and could be optimized out.
- // Just log for now.
- if (chainActive[pindex->nHeight] != pindex) {
- LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
- }
-
- // If the peer's chain has this block, don't inv it back.
- if (!PeerHasHeader(&state, pindex)) {
- pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
- LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
- pto->id, hashToAnnounce.ToString());
- }
- }
- }
- pto->vBlockHashesToAnnounce.clear();
- }
-
- //
- // Message: inventory
- //
- vector<CInv> vInv;
- {
- LOCK(pto->cs_inventory);
- vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX));
-
- // Add blocks
- BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) {
- vInv.push_back(CInv(MSG_BLOCK, hash));
- if (vInv.size() == MAX_INV_SZ) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
- vInv.clear();
- }
- }
- pto->vInventoryBlockToSend.clear();
-
- // Check whether periodic sends should happen
- bool fSendTrickle = pto->fWhitelisted;
- if (pto->nNextInvSend < nNow) {
- fSendTrickle = true;
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
- }
-
- // Time to send but the peer has requested we not relay transactions.
- if (fSendTrickle) {
- LOCK(pto->cs_filter);
- if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
- }
-
- // Respond to BIP35 mempool requests
- if (fSendTrickle && pto->fSendMempool) {
- auto vtxinfo = mempool.infoAll();
- pto->fSendMempool = false;
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
- }
-
- LOCK(pto->cs_filter);
-
- for (const auto& txinfo : vtxinfo) {
- const uint256& hash = txinfo.tx->GetHash();
- CInv inv(MSG_TX, hash);
- pto->setInventoryTxToSend.erase(hash);
- if (filterrate) {
- if (txinfo.feeRate.GetFeePerK() < filterrate)
- continue;
- }
- if (pto->pfilter) {
- if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
- }
- pto->filterInventoryKnown.insert(hash);
- vInv.push_back(inv);
- if (vInv.size() == MAX_INV_SZ) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
- vInv.clear();
- }
- }
- pto->timeLastMempoolReq = GetTime();
- }
-
- // Determine transactions to relay
- if (fSendTrickle) {
- // Produce a vector with all candidates for sending
- vector<std::set<uint256>::iterator> vInvTx;
- vInvTx.reserve(pto->setInventoryTxToSend.size());
- for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
- vInvTx.push_back(it);
- }
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
- }
- // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
- // A heap is used so that not all items need sorting if only a few are being sent.
- CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
- std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- // No reason to drain out at many times the network's capacity,
- // especially since we have many peers and some will draw much shorter delays.
- unsigned int nRelayedTransactions = 0;
- LOCK(pto->cs_filter);
- while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
- // Fetch the top element from the heap
- std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- std::set<uint256>::iterator it = vInvTx.back();
- vInvTx.pop_back();
- uint256 hash = *it;
- // Remove it from the to-be-sent set
- pto->setInventoryTxToSend.erase(it);
- // Check if not in the filter already
- if (pto->filterInventoryKnown.contains(hash)) {
- continue;
- }
- // Not in the mempool anymore? don't bother sending it.
- auto txinfo = mempool.info(hash);
- if (!txinfo.tx) {
- continue;
- }
- if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
- continue;
- }
- if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
- // Send
- vInv.push_back(CInv(MSG_TX, hash));
- nRelayedTransactions++;
- {
- // Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
- {
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
- }
-
- auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
- if (ret.second) {
- vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
- }
- }
- if (vInv.size() == MAX_INV_SZ) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
- vInv.clear();
- }
- pto->filterInventoryKnown.insert(hash);
- }
- }
- }
- if (!vInv.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
-
- // Detect whether we're stalling
- nNow = GetTimeMicros();
- if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
- // Stalling only triggers when the block download window cannot move. During normal steady state,
- // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
- // should only happen during initial block download.
- LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
- pto->fDisconnect = true;
- return true;
- }
- // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
- // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
- // We compensate for other peers to prevent killing off peers due to our own downstream link
- // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
- // to unreasonably increase our timeout.
- if (state.vBlocksInFlight.size() > 0) {
- QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
- if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
- pto->fDisconnect = true;
- return true;
- }
- }
-
- //
- // Message: getdata (blocks)
- //
- vector<CInv> vGetData;
- if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- vector<CBlockIndex*> vToDownload;
- NodeId staller = -1;
- FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
- BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
- LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
- pindex->nHeight, pto->id);
- }
- if (state.nBlocksInFlight == 0 && staller != -1) {
- if (State(staller)->nStallingSince == 0) {
- State(staller)->nStallingSince = nNow;
- LogPrint("net", "Stall started peer=%d\n", staller);
- }
- }
- }
-
- //
- // Message: getdata (non-blocks)
- //
- while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
- {
- const CInv& inv = (*pto->mapAskFor.begin()).second;
- if (!AlreadyHave(inv))
- {
- if (fDebug)
- LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
- vGetData.push_back(inv);
- if (vGetData.size() >= 1000)
- {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- vGetData.clear();
- }
- } else {
- //If we're not going to ask, don't expect a response.
- pto->setAskFor.erase(inv.hash);
- }
- pto->mapAskFor.erase(pto->mapAskFor.begin());
- }
- if (!vGetData.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
-
- //
- // Message: feefilter
- //
- // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
- if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
- CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
- int64_t timeNow = GetTimeMicros();
- if (timeNow > pto->nextSendTimeFeeFilter) {
- CAmount filterToSend = filterRounder.round(currentFilter);
- if (filterToSend != pto->lastSentFeeFilter) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
- pto->lastSentFeeFilter = filterToSend;
- }
- pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
- }
- // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
- // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
- else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
- (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
- pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
- }
- }
- }
- return true;
-}
-
std::string CBlockFileInfo::ToString() const {
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
}
@@ -7069,10 +4107,9 @@ bool LoadMempool(void)
file >> num;
double prioritydummy = 0;
while (num--) {
- CTransaction tx;
int64_t nTime;
int64_t nFeeDelta;
- file >> tx;
+ CTransaction tx(deserialize, file);
file >> nTime;
file >> nFeeDelta;
@@ -7165,9 +4202,5 @@ public:
for (; it1 != mapBlockIndex.end(); it1++)
delete (*it1).second;
mapBlockIndex.clear();
-
- // orphan transactions
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
}
} instance_of_cmaincleanup;
diff --git a/src/main.h b/src/validation.h
index 6b2668e568..28e81d21c6 100644
--- a/src/main.h
+++ b/src/validation.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_MAIN_H
-#define BITCOIN_MAIN_H
+#ifndef BITCOIN_VALIDATION_H
+#define BITCOIN_VALIDATION_H
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
@@ -13,10 +13,9 @@
#include "amount.h"
#include "chain.h"
#include "coins.h"
-#include "net.h"
+#include "protocol.h" // For CMessageHeader::MessageStartChars
#include "script/script_error.h"
#include "sync.h"
-#include "validationinterface.h"
#include "versionbits.h"
#include <algorithm>
@@ -28,7 +27,10 @@
#include <utility>
#include <vector>
+#include <atomic>
+
#include <boost/unordered_map.hpp>
+#include <boost/filesystem/path.hpp>
class CBlockIndex;
class CBlockTreeDB;
@@ -231,7 +233,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
* @return True if state.IsValid()
*/
-bool ProcessNewBlock(const CChainParams& chainparams, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool* fNewBlock);
+bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool* fNewBlock);
/**
* Process incoming block headers.
@@ -274,9 +276,9 @@ bool IsInitialBlockDownload();
*/
std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
-bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
+bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
-bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
+bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
/**
@@ -560,46 +562,4 @@ void DumpMempool();
/** Load the mempool from disk. */
bool LoadMempool();
-// The following things handle network-processing logic
-// (and should be moved to a separate file)
-
-/** Register with a network node to receive its signals */
-void RegisterNodeSignals(CNodeSignals& nodeSignals);
-/** Unregister a network node */
-void UnregisterNodeSignals(CNodeSignals& nodeSignals);
-
-class PeerLogicValidation : public CValidationInterface {
-private:
- CConnman* connman;
-
-public:
- PeerLogicValidation(CConnman* connmanIn);
-
- virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
- virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
- virtual void BlockChecked(const CBlock& block, const CValidationState& state);
-};
-
-struct CNodeStateStats {
- int nMisbehavior;
- int nSyncHeight;
- int nCommonHeight;
- std::vector<int> vHeightInFlight;
-};
-
-/** Get statistics from node state */
-bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch);
-
-/** Process protocol messages received from a given node */
-bool ProcessMessages(CNode* pfrom, CConnman& connman);
-/**
- * Send queued protocol messages to be sent to a give node.
- *
- * @param[in] pto The node which we are sending messages to.
- * @param[in] connman The connection manager for that node.
- */
-bool SendMessages(CNode* pto, CConnman& connman);
-
-#endif // BITCOIN_MAIN_H
+#endif // BITCOIN_VALIDATION_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index bb5337c4ad..008a4ece19 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -6,7 +6,7 @@
#include "chain.h"
#include "rpc/server.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "script/script.h"
#include "script/standard.h"
#include "sync.h"
@@ -267,11 +267,11 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
"2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
);
- CTransaction tx;
+ CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
uint256 hashTx = tx.GetHash();
- CWalletTx wtx(pwalletMain,tx);
+ CWalletTx wtx(pwalletMain, MakeTransactionRef(std::move(tx)));
CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
@@ -304,7 +304,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (pwalletMain->IsMine(tx)) {
+ if (pwalletMain->IsMine(wtx)) {
pwalletMain->AddToWallet(wtx, false);
return NullUniValue;
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 5a22e0278d..9e32b6751d 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -9,7 +9,7 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/rbf.h"
#include "rpc/server.h"
@@ -583,10 +583,10 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
@@ -637,10 +637,10 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
@@ -1149,14 +1149,14 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth)
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
@@ -1780,7 +1780,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
CAmount nCredit = wtx.GetCredit(filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
- CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
+ CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe(filter))
@@ -2420,7 +2420,7 @@ UniValue listunspent(const JSONRPCRequest& request)
continue;
CTxDestination address;
- const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey;
+ const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
if (setAddress.size() && (!fValidAddress || !setAddress.count(address)))
@@ -2445,7 +2445,7 @@ UniValue listunspent(const JSONRPCRequest& request)
}
entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue)));
+ entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)));
entry.push_back(Pair("confirmations", out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
entry.push_back(Pair("solvable", out.fSolvable));
@@ -2557,17 +2557,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
}
// parse hex string from parameter
- CTransaction origTx;
- if (!DecodeHexTx(origTx, request.params[0].get_str(), true))
+ CMutableTransaction tx;
+ if (!DecodeHexTx(tx, request.params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- if (origTx.vout.size() == 0)
+ if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
- if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > origTx.vout.size()))
+ if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
- CMutableTransaction tx(origTx);
CAmount nFeeOut;
string strFailReason;
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index a833be13d0..eb14d176bd 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
{
CMutableTransaction tx(wtx);
--tx.nLockTime; // Just to change the hash :)
- *static_cast<CTransaction*>(&wtx) = CTransaction(tx);
+ wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
@@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
{
CMutableTransaction tx(wtx);
--tx.nLockTime; // Just to change the hash :)
- *static_cast<CTransaction*>(&wtx) = CTransaction(tx);
+ wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index ecbbcb145d..8085706a99 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -42,7 +42,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = new CWalletTx(&wallet, tx);
+ CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->fDebitCached = true;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 39c4fc3f1b..638fca9917 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -13,7 +13,7 @@
#include "consensus/validation.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/block.h"
@@ -75,7 +75,7 @@ struct CompareValueOnly
std::string COutput::ToString() const
{
- return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
+ return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
@@ -400,7 +400,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapTxSpends.count(txin.prevout) <= 1)
continue; // No conflict if zero or one spends
@@ -552,7 +552,7 @@ void CWallet::AddToSpends(const uint256& wtxid)
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
return;
- BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
+ BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin)
AddToSpends(txin.prevout, wtxid);
}
@@ -795,7 +795,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end() && account.vchPubKey.IsValid();
++it)
- BOOST_FOREACH(const CTxOut& txout, (*it).second.vout)
+ BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout)
if (txout.scriptPubKey == scriptPubKey) {
bForceNew = true;
break;
@@ -954,7 +954,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
wtx.BindWallet(this);
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
AddToSpends(hash);
- BOOST_FOREACH(const CTxIn& txin, wtx.vin) {
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) {
if (mapWallet.count(txin.prevout.hash)) {
CWalletTx& prevtx = mapWallet[txin.prevout.hash];
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
@@ -993,7 +993,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
- CWalletTx wtx(this,tx);
+ CWalletTx wtx(this, MakeTransactionRef(tx));
// Get merkle branch if transaction was found in a block
if (posInBlock != -1)
@@ -1052,7 +1052,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapWallet.count(txin.prevout.hash))
mapWallet[txin.prevout.hash].MarkDirty();
@@ -1113,7 +1113,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapWallet.count(txin.prevout.hash))
mapWallet[txin.prevout.hash].MarkDirty();
@@ -1148,8 +1148,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.vout.size())
- return IsMine(prev.vout[txin.prevout.n]);
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
}
return ISMINE_NO;
@@ -1163,9 +1163,9 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.vout.size())
- if (IsMine(prev.vout[txin.prevout.n]) & filter)
- return prev.vout[txin.prevout.n].nValue;
+ if (txin.prevout.n < prev.tx->vout.size())
+ if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)
+ return prev.tx->vout[txin.prevout.n].nValue;
}
}
return 0;
@@ -1380,14 +1380,14 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
CAmount nDebit = GetDebit(filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
- CAmount nValueOut = GetValueOut();
+ CAmount nValueOut = tx->GetValueOut();
nFee = nDebit - nValueOut;
}
// Sent/received.
- for (unsigned int i = 0; i < vout.size(); ++i)
+ for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
- const CTxOut& txout = vout[i];
+ const CTxOut& txout = tx->vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
@@ -1573,7 +1573,7 @@ set<uint256> CWalletTx::GetConflicts() const
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
- if (vin.empty())
+ if (tx->vin.empty())
return 0;
CAmount debit = 0;
@@ -1663,11 +1663,11 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
CAmount nCredit = 0;
uint256 hashTx = GetHash();
- for (unsigned int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < tx->vout.size(); i++)
{
if (!pwallet->IsSpent(hashTx, i))
{
- const CTxOut &txout = vout[i];
+ const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1706,11 +1706,11 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
return nAvailableWatchCreditCached;
CAmount nCredit = 0;
- for (unsigned int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < tx->vout.size(); i++)
{
if (!pwallet->IsSpent(GetHash(), i))
{
- const CTxOut &txout = vout[i];
+ const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1758,23 +1758,23 @@ bool CWalletTx::IsTrusted() const
return false;
// Trusted if all inputs are from us and are in the mempool:
- BOOST_FOREACH(const CTxIn& txin, vin)
+ BOOST_FOREACH(const CTxIn& txin, tx->vin)
{
// Transactions not sent by us: not trusted
const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
if (parent == NULL)
return false;
- const CTxOut& parentOut = parent->vout[txin.prevout.n];
+ const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
return false;
}
return true;
}
-bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const
+bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
{
- CMutableTransaction tx1 = *this;
- CMutableTransaction tx2 = tx;
+ CMutableTransaction tx1 = *this->tx;
+ CMutableTransaction tx2 = *_tx.tx;
for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript();
for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript();
return CTransaction(tx1) == CTransaction(tx2);
@@ -1957,10 +1957,10 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- isminetype mine = IsMine(pcoin->vout[i]);
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
+ isminetype mine = IsMine(pcoin->tx->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
- !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
+ !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) &&
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i))))
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
@@ -2043,7 +2043,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
continue;
int i = output.i;
- CAmount n = pcoin->vout[i].nValue;
+ CAmount n = pcoin->tx->vout[i].nValue;
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
@@ -2130,7 +2130,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
{
if (!out.fSpendable)
continue;
- nValueRet += out.tx->vout[out.i].nValue;
+ nValueRet += out.tx->tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
@@ -2150,9 +2150,9 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
{
const CWalletTx* pcoin = &it->second;
// Clearly invalid input, fail
- if (pcoin->vout.size() <= outpoint.n)
+ if (pcoin->tx->vout.size() <= outpoint.n)
return false;
- nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue;
+ nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
setPresetCoins.insert(make_pair(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
@@ -2208,10 +2208,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
return false;
if (nChangePosInOut != -1)
- tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]);
+ tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
// Add new txins (keeping original txin scriptSig/order)
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (!coinControl.IsSelected(txin.prevout))
{
@@ -2351,7 +2351,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
+ CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
//The coin age after the next block (depth+1) is used instead of the current,
//reflecting an assumption the user would accept a bit more delay for
//a chance at a free transaction.
@@ -2466,10 +2466,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
{
bool signSuccess;
- const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
SignatureData sigdata;
if (sign)
- signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
+ signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
else
signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
@@ -2494,16 +2494,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
// Embed the constructed transaction data in wtxNew.
- *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
+ wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
// Limit size
- if (GetTransactionWeight(txNew) >= MAX_STANDARD_TX_WEIGHT)
+ if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
{
strFailReason = _("Transaction too large");
return false;
}
- dPriority = wtxNew.ComputePriority(dPriority, nBytes);
+ dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);
// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
@@ -2555,7 +2555,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
{
{
LOCK2(cs_main, cs_wallet);
- LogPrintf("CommitTransaction:\n%s", wtxNew.ToString());
+ LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString());
{
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
@@ -2565,7 +2565,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
AddToWallet(wtxNew);
// Notify that old coins are spent
- BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
@@ -2939,15 +2939,15 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
{
CTxDestination addr;
- if (!IsMine(pcoin->vout[i]))
+ if (!IsMine(pcoin->tx->vout[i]))
continue;
- if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
+ if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
+ CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -2969,16 +2969,16 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
{
CWalletTx *pcoin = &walletEntry.second;
- if (pcoin->vin.size() > 0)
+ if (pcoin->tx->vin.size() > 0)
{
bool any_mine = false;
// group all input addresses with each other
- BOOST_FOREACH(CTxIn txin, pcoin->vin)
+ BOOST_FOREACH(CTxIn txin, pcoin->tx->vin)
{
CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
continue;
- if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address))
+ if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address))
continue;
grouping.insert(address);
any_mine = true;
@@ -2987,7 +2987,7 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
// group change with input addresses
if (any_mine)
{
- BOOST_FOREACH(CTxOut txout, pcoin->vout)
+ BOOST_FOREACH(CTxOut txout, pcoin->tx->vout)
if (IsChange(txout))
{
CTxDestination txoutAddr;
@@ -3004,11 +3004,11 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
}
// group lone addrs by themselves
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- if (IsMine(pcoin->vout[i]))
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
+ if (IsMine(pcoin->tx->vout[i]))
{
CTxDestination address;
- if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address))
+ if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address))
continue;
grouping.insert(address);
groupings.insert(grouping);
@@ -3275,7 +3275,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
// ... which are already in a block
int nHeight = blit->second->nHeight;
- BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
+ BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) {
// iterate over all their outputs
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
BOOST_FOREACH(const CKeyID &keyid, vAffected) {
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 409d817046..f7103b6a80 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -161,13 +161,14 @@ struct COutputEntry
};
/** A transaction with a merkle branch linking it to the block chain. */
-class CMerkleTx : public CTransaction
+class CMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
+ CTransactionRef tx;
uint256 hashBlock;
/* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
@@ -179,26 +180,37 @@ public:
CMerkleTx()
{
+ SetTx(MakeTransactionRef());
Init();
}
- CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
+ CMerkleTx(CTransactionRef arg)
{
+ SetTx(std::move(arg));
Init();
}
+ /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
+ * TODO: adapt callers and remove this operator. */
+ operator const CTransaction&() const { return *tx; }
+
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
+ void SetTx(CTransactionRef arg)
+ {
+ tx = std::move(arg);
+ }
+
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
- READWRITE(*(CTransaction*)this);
+ READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
@@ -221,6 +233,9 @@ public:
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
+
+ const uint256& GetHash() const { return tx->GetHash(); }
+ bool IsCoinBase() const { return tx->IsCoinBase(); }
};
/**
@@ -267,17 +282,7 @@ public:
Init(NULL);
}
- CWalletTx(const CWallet* pwalletIn)
- {
- Init(pwalletIn);
- }
-
- CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn)
- {
- Init(pwalletIn);
- }
-
- CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn)
+ CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
{
Init(pwalletIn);
}
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 43fd6a20ad..0395e4a072 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -7,7 +7,7 @@
#include "base58.h"
#include "consensus/validation.h"
-#include "main.h" // For CheckTransaction
+#include "validation.h" // For CheckTransaction
#include "protocol.h"
#include "serialize.h"
#include "sync.h"
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index a0196fe184..a7e20a8356 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -6,7 +6,7 @@
#include "zmqpublishnotifier.h"
#include "version.h"
-#include "main.h"
+#include "validation.h"
#include "streams.h"
#include "util.h"
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index b6c907980f..99d42ab526 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -3,8 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
+#include "streams.h"
#include "zmqpublishnotifier.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;