aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp390
1 files changed, 349 insertions, 41 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 361526f337..ae195ecc40 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -7,6 +7,7 @@
#include "addrman.h"
#include "arith_uint256.h"
+#include "blockencodings.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -207,11 +208,15 @@ namespace {
/** 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.
+ 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;
@@ -284,6 +289,10 @@ struct CNodeState {
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
+ bool fProvidesHeaderAndIDs;
CNodeState() {
fCurrentlyConnected = false;
@@ -300,6 +309,8 @@ struct CNodeState {
nBlocksInFlightValidHeaders = 0;
fPreferredDownload = false;
fPreferHeaders = false;
+ fPreferHeaderAndIDs = false;
+ fProvidesHeaderAndIDs = false;
}
};
@@ -368,6 +379,7 @@ void FinalizeNode(NodeId nodeid) {
// 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()) {
@@ -391,17 +403,26 @@ bool MarkBlockAsReceived(const uint256& hash) {
}
// Requires cs_main.
-void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) {
+// 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);
- QueuedBlock newentry = {hash, pindex, pindex != NULL};
- list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
+ 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 += newentry.fValidatedHeaders;
+ state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
state->nDownloadingSince = GetTimeMicros();
@@ -409,7 +430,10 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa
if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) {
nPeersWithValidatedDownloads++;
}
- mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
+ 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. */
@@ -445,6 +469,28 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
+void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) {
+ if (nodestate->fProvidesHeaderAndIDs) {
+ BOOST_FOREACH(const NodeId nodeid, lNodesAnnouncingHeaderAndIDs)
+ if (nodeid == pfrom->GetId())
+ return;
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 1;
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
+ // As per BIP152, we only get 3 of our peers to announce
+ // blocks using compact encodings.
+ CNode* pnodeStop = FindNode(lNodesAnnouncingHeaderAndIDs.front());
+ if (pnodeStop) {
+ pnodeStop->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
+ lNodesAnnouncingHeaderAndIDs.pop_front();
+ }
+ }
+ fAnnounceUsingCMPCTBLOCK = true;
+ pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ }
+}
+
// Requires cs_main
bool CanDirectFetch(const Consensus::Params &consensusParams)
{
@@ -2272,7 +2318,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
- if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), !fJustCheck, !fJustCheck))
+ if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
// verify that the view's current state corresponds to the previous block
@@ -3310,20 +3356,16 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
return true;
}
-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW)
+bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
- // Check timestamp
- if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60)
- return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
-
return true;
}
-bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW, bool fCheckMerkleRoot)
+bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
@@ -3332,7 +3374,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// Check that the header is valid (particularly PoW). This is mostly
// redundant with the call in AcceptBlockHeader.
- if (!CheckBlockHeader(block, state, consensusParams, nAdjustedTime, fCheckPOW))
+ if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
return false;
// Check the merkle root.
@@ -3398,7 +3440,7 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati
return true;
}
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev)
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime)
{
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
@@ -3408,6 +3450,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early");
+ // Check timestamp
+ if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60)
+ return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
+
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades
if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
@@ -3472,7 +3518,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return true;
}
- if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), GetAdjustedTime()))
+ if (!CheckBlockHeader(block, state, chainparams.GetConsensus()))
return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
// Get prev block index
@@ -3488,7 +3534,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash))
return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str());
- if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev))
+ if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
}
if (pindex == NULL)
@@ -3621,9 +3667,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
indexDummy.nHeight = pindexPrev->nHeight + 1;
// NOTE: CheckBlockHeader is called by CheckBlock
- if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev))
+ if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
- if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), fCheckPOW, fCheckMerkleRoot))
+ if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
@@ -3968,7 +4014,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
- if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime()))
+ if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus()))
return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__,
pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
// check level 2: verify undo validity
@@ -4506,7 +4552,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
boost::this_thread::interruption_point();
it++;
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK)
{
bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
@@ -4548,7 +4594,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
assert(!"cannot load block from disk");
if (inv.type == MSG_BLOCK)
pfrom->PushMessage(NetMsgType::BLOCK, block);
- else // MSG_FILTERED_BLOCK)
+ else if (inv.type == MSG_FILTERED_BLOCK)
{
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
@@ -4568,6 +4614,18 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// else
// no response
}
+ else if (inv.type == MSG_CMPCT_BLOCK)
+ {
+ // If a peer is asking for old blocks, we're almost guaranteed
+ // they wont have a useful mempool to match against a compact block,
+ // and we dont feel like constructing the object for them, so
+ // instead we respond with the full, non-compact block.
+ if (mi->second->nHeight >= chainActive.Height() - 10) {
+ CBlockHeaderAndShortTxIDs cmpctblock(block);
+ pfrom->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock);
+ } else
+ pfrom->PushMessage(NetMsgType::BLOCK, block);
+ }
// Trigger the peer node to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom->hashContinue)
@@ -4607,7 +4665,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// Track requests for our stuff.
GetMainSignals().Inventory(inv.hash);
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK)
break;
}
}
@@ -4817,6 +4875,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// nodes)
pfrom->PushMessage(NetMsgType::SENDHEADERS);
}
+ if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ // Tell our peer we are willing to provide version-1 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 = 1;
+ pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
+ }
}
@@ -4891,6 +4959,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
State(pfrom->GetId())->fPreferHeaders = true;
}
+ else if (strCommand == NetMsgType::SENDCMPCT)
+ {
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 1;
+ vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
+ if (nCMPCTBLOCKVersion == 1) {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ }
+ }
+
else if (strCommand == NetMsgType::INV)
{
@@ -4937,7 +5017,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CNodeState *nodestate = State(pfrom->GetId());
if (CanDirectFetch(chainparams.GetConsensus()) &&
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- vToFetch.push_back(inv);
+ if (nodestate->fProvidesHeaderAndIDs)
+ vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash));
+ else
+ vToFetch.push_back(inv);
// Mark block as in flight already, even though the actual "getdata" message only goes out
// later (within the same cs_main lock, though).
MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
@@ -5034,6 +5117,39 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
+ else if (strCommand == NetMsgType::GETBLOCKTXN)
+ {
+ BlockTransactionsRequest req;
+ vRecv >> req;
+
+ BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
+ if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
+ return true;
+ }
+
+ if (it->second->nHeight < chainActive.Height() - 15) {
+ LogPrint("net", "Peer %d sent us a getblocktxn for a block > 15 deep", pfrom->id);
+ 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]];
+ }
+ pfrom->PushMessage(NetMsgType::BLOCKTXN, resp);
+ }
+
+
else if (strCommand == NetMsgType::GETHEADERS)
{
CBlockLocator locator;
@@ -5241,6 +5357,174 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
+ 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())
+ pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256());
+ return true;
+ }
+
+ CBlockIndex *pindex = NULL;
+ CValidationState state;
+ if (!AcceptBlockHeader(cmpctblock.header, state, chainparams, &pindex)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ if (nDoS > 0)
+ Misbehaving(pfrom->GetId(), nDoS);
+ LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
+ return true;
+ }
+ }
+
+ // 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, cmpctblock.header.GetHash());
+ pfrom->PushMessage(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());
+
+ // 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, cmpctblock.header.GetHash());
+ pfrom->PushMessage(NetMsgType::GETDATA, vInv);
+ return true;
+ }
+
+ 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);
+ } else {
+ req.blockhash = pindex->GetBlockHash();
+ pfrom->PushMessage(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, cmpctblock.header.GetHash());
+ pfrom->PushMessage(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);
+ }
+ }
+
+ CheckBlockIndex(chainparams.GetConsensus());
+ }
+
+ else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ BlockTransactions resp;
+ vRecv >> resp;
+
+ 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;
+ CBlock block;
+ 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, resp.blockhash));
+ pfrom->PushMessage(NetMsgType::GETDATA, invs);
+ } else {
+ CValidationState state;
+ ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL);
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
+ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ }
+ }
+ }
+
+
else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
{
std::vector<CBlockHeader> headers;
@@ -5290,10 +5574,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
- if (pindexLast)
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+ assert(pindexLast);
+ UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
- if (nCount == MAX_HEADERS_RESULTS && pindexLast && hasNewHeaders) {
+ if (nCount == MAX_HEADERS_RESULTS && hasNewHeaders) {
// 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.
@@ -5343,6 +5627,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
}
if (vGetData.size() > 0) {
+ if (nodestate->fProvidesHeaderAndIDs && 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);
+ // In any case, we want to download using a compact block, not a regular one
+ vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
+ }
pfrom->PushMessage(NetMsgType::GETDATA, vGetData);
}
}
@@ -5896,7 +6187,9 @@ bool SendMessages(CNode* pto)
// add all to the inv queue.
LOCK(pto->cs_inventory);
vector<CBlock> vHeaders;
- bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
+ 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
@@ -5948,6 +6241,33 @@ bool SendMessages(CNode* pto)
}
}
}
+ 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);
+ pto->PushMessage(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);
+ }
+ pto->PushMessage(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
@@ -5973,18 +6293,6 @@ bool SendMessages(CNode* pto)
pto->id, hashToAnnounce.ToString());
}
}
- } else if (!vHeaders.empty()) {
- 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);
- }
- pto->PushMessage(NetMsgType::HEADERS, vHeaders);
- state.pindexBestHeaderSent = pBestIndex;
}
pto->vBlockHashesToAnnounce.clear();
}