從零開始學習比特幣開發(九)--P2P 網路建立之訊息處理中篇
P2P 網路的建立是在系統啟動的第 12 步,最後時刻呼叫 CConnman::Start
方法開始的。
恭喜你越來越接近比特幣的核心了,在上篇中,我們主要講解了比特幣的訊息處理執行緒,接下來,在下篇中,將以具體的比特幣訊息即比特幣協義分析為主。針對比特幣的協義,為了從邏輯上進行理解,我們並沒有完全按照程式碼的順序,而是按照某個具體的訊息的 請求----響應
模式來進行分析。
下面我們來看比特幣協義相關的程式碼。
1、節點握手處理
1.1、接收 version
訊息
節點作為伺服器,處理客戶端節點發送的版本請求。
版本訊息是每個對等節點都要傳送的訊息,並且是最先發送、只能傳送一次的訊息,對等節點雙方都要傳送這個訊息和下面的確認訊息,只有雙方都發送過版本訊息,並且收到確認訊息,對等節點間才可以進行後續訊息傳送。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 1621 行。具體處理如下:
-
檢查對等節點的版本欄位是否已經設定,如果已經設定,即遠端對等節點已經發送過版本訊息,那麼:在開啟 BIP 161 情況下發送拒絕訊息;對遠端對待節點進行處罰。
if (pfrom->nVersion != 0) { if (enable_bip61) { connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); } LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; }
Misbehaving
方法進行處理,具體處理如下:- 檢查是否要增加節點的不良積分,如果不是,即增加的積分數量為 0,則直接退出。
- 取得節點的狀態物件。如果不存在,則直接退出。
- 把節點的狀態物件的不良積分加上要增加的不良積分。
- 比較節點的不良積分與預設的或使用者通過
-banscore
引數指定的不良積分進行比較。如果在增加當前不良積分後大於等於設定的不良積分,並且增加之前小於設定的不良積分,那麼設定狀態物件為應該禁止,即設定fShouldBan
屬性為真。
這個方法的程式碼如下:
void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (howmuch == 0) return; CNodeState *state = State(pnode); if (state == nullptr) return; state->nMisbehavior += howmuch; int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); std::string message_prefixed = message.empty() ? "" : (": " + message); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed); state->fShouldBan = true; } else LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed); }
-
從輸入流中取得遠端對等節點發送的版本資訊、支援的服務資訊、傳送時間、顯示地址。
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; nSendVersion = std::min(nVersion, PROTOCOL_VERSION); nServices = ServiceFlags(nServiceInt);
-
如果對等節點是出站的,呼叫
CConnman
物件的SetServices
方法,設定對等節點所支援的服務。if (!pfrom->fInbound) { connman->SetServices(pfrom->addr, nServices); }
方法內部最終會獲取節點的地址資訊物件,然後設定其支援的服務屬性。
-
如果對等節點是出站的,且不是臨時的試探者,且不是手動指定的,且與本地服務不匹配,那麼:在開啟 BIP 161 情況下發送拒絕訊息;然後設定遠端對待節點為斷開;然後返回假。
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices)) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); if (enable_bip61) { connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); } pfrom->fDisconnect = true; return false; }
-
如果傳送的版本的小於協義規定的最小版本
MIN_PEER_PROTO_VERSION
,那麼:在開啟 BIP 161 情況下發送拒絕訊息;然後設定遠端對待節點為斷開;然後返回假。if (nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); if (enable_bip61) { 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; }
-
如果輸入流不為空,則從流中依次取得 addrFrom、nNonce、strSubVer(客戶端字串)、nStartingHeight(客戶端區塊鏈的高度)、fRelay等資訊。
if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); cleanSubVer = SanitizeString(strSubVer); } if (!vRecv.empty()) { vRecv >> nStartingHeight; } if (!vRecv.empty()) vRecv >> fRelay;
-
如果對等節點節點是入站節點,且連線到自身,那麼設定遠端對待節點為斷開,並返回真。
if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; return true; }
-
如果對等節點是入站節點,且其地址是可路由的,那麼呼叫
SeenLocal
方法進行處理。if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); }
在
SeenLocal
方法中,如果這個地址在mapLocalHost
集合中存在,那麼設定其對應的LocalServiceInfo
物件的nScore
加1。如晨不存在,則直接返回真。bool SeenLocal(const CService& addr) { { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) return false; mapLocalHost[addr].nScore++; } return true; }
-
如果是對等節點是入站節點,則呼叫
PushNodeVersion
方法,傳送自身的版本資訊給遠端對等節點。節點在收到遠端對待節點發送來的版本訊息,並且經過檢查沒問題之後,自身傳送一個版本訊息給對遠端對待節點。
-
呼叫
CConnman
物件的PushMessage
方法,傳送版本確認包。因為當前的
version
訊息,是別的節點請求我們的,當我們允許其連線時,傳送版本確認包。注意,只有在雙方都發送版本確認包之後,雙方才可以互相傳送訊息。 -
設定對等節點的服務屬性、儲存地址、對等節點執行的客戶端、對等節點區塊鏈的高度、版本等。如果對等節點隔離見證服務,則設定對等節點對應的狀態物件的相關屬性為真。
pfrom->nServices = nServices; pfrom->SetAddrLocal(addrMe); { LOCK(pfrom->cs_SubVer); pfrom->strSubVer = strSubVer; pfrom->cleanSubVer = cleanSubVer; } pfrom->nStartingHeight = nStartingHeight; pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED)); pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); pfrom->fRelayTxes = fRelay; pfrom->SetSendVersion(nSendVersion); pfrom->nVersion = nVersion;
-
呼叫
UpdatePreferredDownload
方法,將對等節點設為可能的首先下載節點。如果節點是出站的或者在白名單中,並且可以提供區塊服務,並且
fOneShot
屬性為假,即可作為首選下載節點。 -
如果對等節點不是入站節點,進行如下處理。
-
如果對等節點不是孤立的,並且不需要進行IBD下載(呼叫
IsInitialBlockDownload
函式進行判斷,通常第一次啟動或在常時間離線,比如24小時,有大師區塊需要下載時,本方法返回真),那麼:- 呼叫
GetLocalAddress
方法,獲取對該對等節點來說是最佳的地址。 - 如果找到的地址是可路由的,那麼呼叫對等節點的
PushAddress
方法,把找到的地址儲存在vAddrToSend
集合中。 - 否則,呼叫
IsPeerAddrLocalGood
測試遠端對等節點看到的我們的外部IP是否可以路由。如果可以路由,那麼呼叫對等節點的PushAddress
方法,把地址儲存在vAddrToSend
集合中。
以上程式碼如下:
if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); FastRandomContext insecure_rand; if (addr.IsRoutable()) { LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(addrMe); LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } }
- 呼叫
-
如果需要,比如:本地儲存的遠端地址少於 1000個,那麼調用
PushMessage
方法,請求遠端節點發送更多的地址,即傳送getaddr
訊息。然後把請求地址的標誌設定為真。if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000) { connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom->fGetAddr = true; }
-
呼叫
MarkAddressGood
方法,儲存遠端對等節點,表明它是可訪問的。connman->MarkAddressGood(pfrom->addr);
-
-
如果遠端對等節點的版本小於 70012,則傳送一個
alert
訊息。if (pfrom->nVersion <= 70012) { CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); }
-
如果節點是臨時引導節點,則斷開節點,即設定節點的斷開屬性為真。
if (pfrom->fFeeler) { assert(pfrom->fInbound == false); pfrom->fDisconnect = true; }
-
版本訊息處理完成,返回真。
1.2、接收 verack
訊息
節點作為客戶端,處理伺服器節點發送的版本響應訊息,即版本確認訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 1805 行。具體處理過程如下:
-
設定接收到的版本確認訊息中的版本號。
pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
-
如果對等節點不是入站節點,設定對等節點的狀態物件的當前連線屬性為真。
if (!pfrom->fInbound) { LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; }
-
如果對等節點的版本大於支援使用區塊頭部來公告區塊的最小版本(
SENDHEADERS_VERSION = 70012
),那麼:呼叫
PushMessage
方法傳送sendheaders
訊息,通知遠端對等節點我們更願意通過headers
訊息來接收新區塊的公告,而不是inv
訊息。這樣以後當有新區塊需要公告時,遠端對等就會通過
headers
訊息把區塊頭部先發送給我們,當我們再次請求時才會傳送完整的區塊。 -
如果對等節點的版本大於支援緊湊區塊的最小版本(
SHORT_IDS_BLOCKS_VERSION = 70014
),那麼分兩種情況處理。- 第一種情況,如果同時支援閃電網路,那麼給對等節點發送一個緊湊區塊版本為 2 的
sendcmpct
訊息。 - 第二種情況,如果不支援閃電網路,那麼給對等節點發送一個緊湊區塊版本為 1 的
sendcmpct
訊息。
無論哪一種情況,遠端對等節點以後都會向本節點發送緊湊區塊。
程式碼如下:
if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { 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)); }
- 第一種情況,如果同時支援閃電網路,那麼給對等節點發送一個緊湊區塊版本為 2 的
-
設定對等節點完全成功連線的標誌為真,然後返回真。
pfrom->fSuccessfullyConnected = true; return true;
只有在對等節點雙方都各自發送版本訊息和確認訊息之後,雙方才真正建立起連線關係,才可以進行後續的互動,比如請求資料訊息等。
2、保持連線的處理
因為在比特幣網路中,任何一個節點都可以隨時加入網路,也可以隨時離開網路,所以兩個連線的節點需要定時互相傳送 ping
和 pong
來確保接點可以連線,如果在特定的時間內沒有 ping
訊息,節點即可認為連線已經斷開。
2.1、ping
訊息
這個訊息比較簡單,不作具體解釋,程式碼如下:
if (strCommand == NetMsgType::PING) {
if (pfrom->nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
vRecv >> nonce;
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
return true;
}
2.1、pong
訊息
這個訊息也比較簡單,不作具體解釋,程式碼如下:
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;
if (pfrom->nPingNonceSent != 0) {
if (nonce == pfrom->nPingNonceSent) {
bPingFinished = true;
int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
if (pingUsecTime > 0) {
pfrom->nPingUsecTime = pingUsecTime;
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime);
} else {
sProblem = "Timing mishap";
}
} else {
sProblem = "Nonce mismatch";
if (nonce == 0) {
bPingFinished = true;
sProblem = "Nonce zero";
}
}
} else {
sProblem = "Unsolicited pong without ping";
}
} else {
bPingFinished = true;
sProblem = "Short payload";
}
if (bPingFinished) {
pfrom->nPingNonceSent = 0;
}
return true;
}
3、獲取更多地址的處理
如果對等節點需要更多地址時,會發送 getaddr
訊息請求遠端對等節點發送更多的地址,當遠端對等節點收到請求後,會通過傳送 addr
訊息傳遞更多的地址。
3.1、getaddr
訊息
節點作為伺服器,響應客戶端節點發送的請求地址訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 2728 行。具體處理如下:
-
如果對等節點不是入站節點,則忽略該請求,並返回。
if (!pfrom->fInbound) { return true; }
-
如果對等節點已傳送過請求地址,即遠端對等節點重複請求地址,則忽略該請求,並返回。
if (pfrom->fSentAddr) { return true; }
-
設定對等節點已傳送過請求地址標誌為真。清空對等節點的
vAddrToSend
集合。pfrom->fSentAddr = true; pfrom->vAddrToSend.clear();
-
呼叫
GetAddresses
方法,返回要傳送的地址。從地址管理器隨機找到 N 個地址,N不能大於最大值 2500,並且這些地址的狀態都比較好。
-
遍歷要傳送的節點,呼叫對等節點的
PushAddress
方法,把要傳送的地址儲存到vAddrToSend
向量中。由執行緒進行定時傳送
addr
訊息。 -
返回真。
3.2、addr
訊息
節點作為客戶端,響應伺服器節點返回的地址。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 1849 行。具體處理如下:
-
從輸入流中取得遠端對等節點發送的地址列表儲存到
vAddr
向量中。std::vector<CAddress> vAddr; vRecv >> vAddr;
-
如果遠端對等節點的版本小於 31402(在這種版本比較老的情況下,我們只在初始時接收 DNS 種子伺服器傳送的地址),並且本儲存的地址已經超過 1000,則直接返回真。
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000) return true;
-
如果遠端對等節點發送的地址超過 1000,呼叫
Misbehaving
方法,對遠端節點進行懲罰,可能導致其被禁止傳送。if (vAddr.size() > 1000) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 20, strprintf("message addr size() = %u", vAddr.size())); return false; }
-
遍歷所有的地址列表,進行如下處理:
-
如果執行緒被中止,則返回真。
-
如果代表的節點不支援
NODE_NETWORK
、NODE_NETWORK_LIMITED
兩者之一的服務,則處理下一個。if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices)) continue;
-
設定地址的時間屬性
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60;
-
呼叫對等節點的
AddAddressKnown
方法,把當前地址儲存到已知地址addrKnown
中。 -
如果地址是可路由的,則加入
vAddrOk
列表中。if (fReachable) vAddrOk.push_back(addr);
-
-
呼叫
CConnman::AddNewAddresses
方法,儲存vAddrOk
列表中的地址。AddNewAddresses
方法最終把地址列表儲存在CAddrMan
物件的mapInfo
屬性中。 -
如果傳送的地址數量少於 1000,設定對等節點的獲取地址標誌為假。以便以後再次獲取地址。如果對等節點的
fOneShot
屬性為真,則設定對等節點的斷開連線標誌為真。if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) pfrom->fDisconnect = true;
-
返回真。
4、sendheaders
訊息
節點作為伺服器,響應客戶端節點發送的更願意接收頭部而不是區塊體的設定訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 1899 行。這個訊息的處理比較簡單,只把節點對應的狀態物件的 fPreferHeaders
屬性為真。
程式碼如下:
if (strCommand == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom->GetId())->fPreferHeaders = true;
return true;
}
5、sendcmpct
訊息
節點作為伺服器,響應客戶端節點發送的接收緊湊區塊而不是普通區塊的設定訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 1905 行。
以 if (strCommand == NetMsgType::SENDCMPCT) {
為開始,具體如下:
-
從輸入流中取得
fAnnounceUsingCMPCTBLOCK
、nCMPCTBLOCKVersion
等引數。vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
-
如果緊湊區塊版本
nCMPCTBLOCKVersion
等於 1 ,或者節點可以響應包含隔離見證的區塊和交易請求,且nCMPCTBLOCKVersion
等於2,那麼進行下面的處理。if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) { LOCK(cs_main); 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); } }
-
返回真。
6、feefilter
訊息
節點作為伺服器,響應客戶端節點發送的費率過濾設定訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 2911 行。
以 if (strCommand == NetMsgType::FEEFILTER) {
為開始,這個處理比較簡單,程式碼如下:
if (strCommand == NetMsgType::FEEFILTER) {
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
{
LOCK(pfrom->cs_feeFilter);
pfrom->minFeeFilter = newFeeFilter;
}
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
}
return true;
}
其中 MoneyRange
方法檢查費率引數是否在 0 到 2100 萬比特幣之間。
7、filterload
訊息
節點作為伺服器,響應 SPV 節點發送的布隆過濾設定訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 2851 行。
以 if (strCommand == NetMsgType::FILTERLOAD) {
為開始,具體如下:
-
從輸入流中取得過濾器引數。
CBloomFilter filter; vRecv >> filter;
-
呼叫布隆過濾器的
IsWithinSizeConstraints
方法,檢查過濾器的是否超過限制區間。如果超過,則呼叫Misbehaving
方法,對遠端對等節點進行設定,可能導致其被禁止。如果不超過,則:- 生成一個新的
CBloomFilter
過濾器物件,並設定節點的過濾器屬性pfilter
為新生成的物件。 - 呼叫節點過濾器的
UpdateEmptyFull
方法,重置其內部屬性vData
。 - 設定中繼交易屬性
fRelayTxes
為真。
以上程式碼如下:
if (!filter.IsWithinSizeConstraints()) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); pfrom->pfilter.reset(new CBloomFilter(filter)); pfrom->pfilter->UpdateEmptyFull(); pfrom->fRelayTxes = true; }
- 生成一個新的
-
返回真。
8、filteradd
訊息
節點作為伺服器,響應 SPV 節點發送的增加布隆過濾訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 2871 行。
以 if (strCommand == NetMsgType::FILTERADD) {
為開始,具體如下:
-
從輸入流中取得要增加的過濾器。
std::vector<unsigned char> vData; vRecv >> vData;
-
如果傳送的位元組數量大於 520,則設定變數
bad
為真。如果不大於,則進行下面的判斷。如果已經發送過
filterload
訊息,則把新的過濾器儲存到過濾器集合中。否則,設定變數bad
為真。程式碼如下:
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; } }
-
如果變數為真,呼叫
Misbehaving
方法,懲罰節點。 -
返回真。
9、filterclear
節點作為伺服器,響應 SPV 節點發送的增加布隆過濾訊息。
程式碼在 net_processing.cpp
檔案中的 ProcessMessage
方法的 2895 行。
以 if (strCommand == NetMsgType::FILTERCLEAR) {
為開始,這個訊息處理比較簡單,程式碼如下,可以自己理解。
if (strCommand == NetMsgType::FILTERCLEAR) {
LOCK(pfrom->cs_filter);
if (pfrom->GetLocalServices() & NODE_BLOOM) {
pfrom->pfilter.reset(new CBloomFilter());
}
pfrom->fRelayTxes = true;
return true;
}
我是區小白,Ulord全球社群聯盟(優得社群)核心區塊鏈技術開發者,深入研究比特幣,以太坊,EOS Dash,Rsk,Java, Nodejs,PHP,Python,C++ 我希望能聚集更多區塊鏈開發者,一起學習共同進步。p