|
|
|
@ -3029,6 +3029,115 @@ bool static AlreadyHave(const CInv& inv)
@@ -3029,6 +3029,115 @@ bool static AlreadyHave(const CInv& inv)
|
|
|
|
|
unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void static ProcessGetData(CNode* pfrom) |
|
|
|
|
{ |
|
|
|
|
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); |
|
|
|
|
|
|
|
|
|
vector<CInv> vNotFound; |
|
|
|
|
|
|
|
|
|
while (it != pfrom->vRecvGetData.end()) { |
|
|
|
|
// Don't bother if send buffer is too full to respond anyway
|
|
|
|
|
if (pfrom->nSendSize >= SendBufferSize()) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
const CInv &inv = *it; |
|
|
|
|
{ |
|
|
|
|
if (fShutdown) |
|
|
|
|
break; |
|
|
|
|
it++; |
|
|
|
|
|
|
|
|
|
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) |
|
|
|
|
{ |
|
|
|
|
// Send block from disk
|
|
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); |
|
|
|
|
if (mi != mapBlockIndex.end()) |
|
|
|
|
{ |
|
|
|
|
CBlock block; |
|
|
|
|
block.ReadFromDisk((*mi).second); |
|
|
|
|
if (inv.type == MSG_BLOCK) |
|
|
|
|
pfrom->PushMessage("block", block); |
|
|
|
|
else // MSG_FILTERED_BLOCK)
|
|
|
|
|
{ |
|
|
|
|
LOCK(pfrom->cs_filter); |
|
|
|
|
if (pfrom->pfilter) |
|
|
|
|
{ |
|
|
|
|
CMerkleBlock merkleBlock(block, *pfrom->pfilter); |
|
|
|
|
pfrom->PushMessage("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 didnt 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) |
|
|
|
|
if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) |
|
|
|
|
pfrom->PushMessage("tx", block.vtx[pair.first]); |
|
|
|
|
} |
|
|
|
|
// else
|
|
|
|
|
// no response
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Trigger them 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, hashBestChain)); |
|
|
|
|
pfrom->PushMessage("inv", vInv); |
|
|
|
|
pfrom->hashContinue = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (inv.IsKnownType()) |
|
|
|
|
{ |
|
|
|
|
// Send stream from relay memory
|
|
|
|
|
bool pushed = false; |
|
|
|
|
{ |
|
|
|
|
LOCK(cs_mapRelay); |
|
|
|
|
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv); |
|
|
|
|
if (mi != mapRelay.end()) { |
|
|
|
|
pfrom->PushMessage(inv.GetCommand(), (*mi).second); |
|
|
|
|
pushed = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!pushed && inv.type == MSG_TX) { |
|
|
|
|
LOCK(mempool.cs); |
|
|
|
|
if (mempool.exists(inv.hash)) { |
|
|
|
|
CTransaction tx = mempool.lookup(inv.hash); |
|
|
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|
|
|
|
ss.reserve(1000); |
|
|
|
|
ss << tx; |
|
|
|
|
pfrom->PushMessage("tx", ss); |
|
|
|
|
pushed = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!pushed) { |
|
|
|
|
vNotFound.push_back(inv); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Track requests for our stuff.
|
|
|
|
|
Inventory(inv.hash); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
pfrom->PushMessage("notfound", vNotFound); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) |
|
|
|
|
{ |
|
|
|
|
RandAddSeedPerfmon(); |
|
|
|
@ -3104,7 +3213,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
@@ -3104,7 +3213,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|
|
|
|
|
|
|
|
|
// Change version
|
|
|
|
|
pfrom->PushMessage("verack"); |
|
|
|
|
pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); |
|
|
|
|
pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); |
|
|
|
|
|
|
|
|
|
if (!pfrom->fInbound) |
|
|
|
|
{ |
|
|
|
@ -3168,7 +3277,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
@@ -3168,7 +3277,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|
|
|
|
|
|
|
|
|
else if (strCommand == "verack") |
|
|
|
|
{ |
|
|
|
|
pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); |
|
|
|
|
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3302,101 +3411,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
@@ -3302,101 +3411,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|
|
|
|
if (fDebugNet || (vInv.size() != 1)) |
|
|
|
|
printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); |
|
|
|
|
|
|
|
|
|
vector<CInv> vNotFound; |
|
|
|
|
BOOST_FOREACH(const CInv& inv, vInv) |
|
|
|
|
{ |
|
|
|
|
if (fShutdown) |
|
|
|
|
return true; |
|
|
|
|
if (fDebugNet || (vInv.size() == 1)) |
|
|
|
|
printf("received getdata for: %s\n", inv.ToString().c_str()); |
|
|
|
|
if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) |
|
|
|
|
printf("received getdata for: %s\n", vInv[0].ToString().c_str()); |
|
|
|
|
|
|
|
|
|
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) |
|
|
|
|
{ |
|
|
|
|
// Send block from disk
|
|
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); |
|
|
|
|
if (mi != mapBlockIndex.end()) |
|
|
|
|
{ |
|
|
|
|
CBlock block; |
|
|
|
|
block.ReadFromDisk((*mi).second); |
|
|
|
|
if (inv.type == MSG_BLOCK) |
|
|
|
|
pfrom->PushMessage("block", block); |
|
|
|
|
else // MSG_FILTERED_BLOCK)
|
|
|
|
|
{ |
|
|
|
|
LOCK(pfrom->cs_filter); |
|
|
|
|
if (pfrom->pfilter) |
|
|
|
|
{ |
|
|
|
|
CMerkleBlock merkleBlock(block, *pfrom->pfilter); |
|
|
|
|
pfrom->PushMessage("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 didnt 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) |
|
|
|
|
if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) |
|
|
|
|
pfrom->PushMessage("tx", block.vtx[pair.first]); |
|
|
|
|
} |
|
|
|
|
// else
|
|
|
|
|
// no response
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Trigger them 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, hashBestChain)); |
|
|
|
|
pfrom->PushMessage("inv", vInv); |
|
|
|
|
pfrom->hashContinue = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (inv.IsKnownType()) |
|
|
|
|
{ |
|
|
|
|
// Send stream from relay memory
|
|
|
|
|
bool pushed = false; |
|
|
|
|
{ |
|
|
|
|
LOCK(cs_mapRelay); |
|
|
|
|
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv); |
|
|
|
|
if (mi != mapRelay.end()) { |
|
|
|
|
pfrom->PushMessage(inv.GetCommand(), (*mi).second); |
|
|
|
|
pushed = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!pushed && inv.type == MSG_TX) { |
|
|
|
|
LOCK(mempool.cs); |
|
|
|
|
if (mempool.exists(inv.hash)) { |
|
|
|
|
CTransaction tx = mempool.lookup(inv.hash); |
|
|
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|
|
|
|
ss.reserve(1000); |
|
|
|
|
ss << tx; |
|
|
|
|
pfrom->PushMessage("tx", ss); |
|
|
|
|
pushed = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!pushed) { |
|
|
|
|
vNotFound.push_back(inv); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Track requests for our stuff.
|
|
|
|
|
Inventory(inv.hash); |
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
pfrom->PushMessage("notfound", vNotFound); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); |
|
|
|
|
ProcessGetData(pfrom); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3705,13 +3724,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
@@ -3705,13 +3724,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// requires LOCK(cs_vRecvMsg)
|
|
|
|
|
bool ProcessMessages(CNode* pfrom) |
|
|
|
|
{ |
|
|
|
|
CDataStream& vRecv = pfrom->vRecv; |
|
|
|
|
if (vRecv.empty()) |
|
|
|
|
return true; |
|
|
|
|
//if (fDebug)
|
|
|
|
|
// printf("ProcessMessages(%u bytes)\n", vRecv.size());
|
|
|
|
|
// printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Message format
|
|
|
|
@ -3721,33 +3738,41 @@ bool ProcessMessages(CNode* pfrom)
@@ -3721,33 +3738,41 @@ bool ProcessMessages(CNode* pfrom)
|
|
|
|
|
// (4) checksum
|
|
|
|
|
// (x) data
|
|
|
|
|
//
|
|
|
|
|
bool fOk = true; |
|
|
|
|
|
|
|
|
|
loop |
|
|
|
|
{ |
|
|
|
|
if (!pfrom->vRecvGetData.empty()) |
|
|
|
|
ProcessGetData(pfrom); |
|
|
|
|
|
|
|
|
|
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->vSend.size() >= SendBufferSize()) |
|
|
|
|
if (pfrom->nSendSize >= SendBufferSize()) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
// get next message
|
|
|
|
|
CNetMessage& msg = *it; |
|
|
|
|
|
|
|
|
|
//if (fDebug)
|
|
|
|
|
// printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n",
|
|
|
|
|
// 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
|
|
|
|
|
CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); |
|
|
|
|
int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); |
|
|
|
|
if (vRecv.end() - pstart < nHeaderSize) |
|
|
|
|
{ |
|
|
|
|
if ((int)vRecv.size() > nHeaderSize) |
|
|
|
|
{ |
|
|
|
|
printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); |
|
|
|
|
vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); |
|
|
|
|
} |
|
|
|
|
if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { |
|
|
|
|
printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); |
|
|
|
|
fOk = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if (pstart - vRecv.begin() > 0) |
|
|
|
|
printf("\n\nPROCESSMESSAGE SKIPPED %"PRIpdd" BYTES\n\n", pstart - vRecv.begin()); |
|
|
|
|
vRecv.erase(vRecv.begin(), pstart); |
|
|
|
|
|
|
|
|
|
// Read header
|
|
|
|
|
vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); |
|
|
|
|
CMessageHeader hdr; |
|
|
|
|
vRecv >> hdr; |
|
|
|
|
CMessageHeader& hdr = msg.hdr; |
|
|
|
|
if (!hdr.IsValid()) |
|
|
|
|
{ |
|
|
|
|
printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); |
|
|
|
@ -3757,19 +3782,9 @@ bool ProcessMessages(CNode* pfrom)
@@ -3757,19 +3782,9 @@ bool ProcessMessages(CNode* pfrom)
|
|
|
|
|
|
|
|
|
|
// Message size
|
|
|
|
|
unsigned int nMessageSize = hdr.nMessageSize; |
|
|
|
|
if (nMessageSize > MAX_SIZE) |
|
|
|
|
{ |
|
|
|
|
printf("ProcessMessages(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (nMessageSize > vRecv.size()) |
|
|
|
|
{ |
|
|
|
|
// Rewind and wait for rest of message
|
|
|
|
|
vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Checksum
|
|
|
|
|
CDataStream& vRecv = msg.vRecv; |
|
|
|
|
uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); |
|
|
|
|
unsigned int nChecksum = 0; |
|
|
|
|
memcpy(&nChecksum, &hash, sizeof(nChecksum)); |
|
|
|
@ -3780,20 +3795,16 @@ bool ProcessMessages(CNode* pfrom)
@@ -3780,20 +3795,16 @@ bool ProcessMessages(CNode* pfrom)
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Copy message to its own buffer
|
|
|
|
|
CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); |
|
|
|
|
vRecv.ignore(nMessageSize); |
|
|
|
|
|
|
|
|
|
// Process message
|
|
|
|
|
bool fRet = false; |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
{ |
|
|
|
|
LOCK(cs_main); |
|
|
|
|
fRet = ProcessMessage(pfrom, strCommand, vMsg); |
|
|
|
|
fRet = ProcessMessage(pfrom, strCommand, vRecv); |
|
|
|
|
} |
|
|
|
|
if (fShutdown) |
|
|
|
|
return true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
catch (std::ios_base::failure& e) |
|
|
|
|
{ |
|
|
|
@ -3822,8 +3833,11 @@ bool ProcessMessages(CNode* pfrom)
@@ -3822,8 +3833,11 @@ bool ProcessMessages(CNode* pfrom)
|
|
|
|
|
printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vRecv.Compact(); |
|
|
|
|
return true; |
|
|
|
|
// In case the connection got shut down, its receive buffer was wiped
|
|
|
|
|
if (!pfrom->fDisconnect) |
|
|
|
|
pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); |
|
|
|
|
|
|
|
|
|
return fOk; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3837,7 +3851,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
@@ -3837,7 +3851,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|
|
|
|
|
|
|
|
|
// Keep-alive ping. We send a nonce of zero because we don't use it anywhere
|
|
|
|
|
// right now.
|
|
|
|
|
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) { |
|
|
|
|
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) { |
|
|
|
|
uint64 nonce = 0; |
|
|
|
|
if (pto->nVersion > BIP0031_VERSION) |
|
|
|
|
pto->PushMessage("ping", nonce); |
|
|
|
|