上一篇整體上學(xué)習(xí)了net_plugin插件可霎,但是具體的內(nèi)容沒有寫科乎。從這篇筆記開始,重點(diǎn)分析各節(jié)點(diǎn)之間是如何通過p2p插件同步區(qū)塊的前酿。首先患雏,啟動(dòng)一個(gè)節(jié)點(diǎn)之后,當(dāng)前節(jié)點(diǎn)向遠(yuǎn)程節(jié)點(diǎn)發(fā)送的第一個(gè)數(shù)據(jù)包就是handshake_message類型的(time時(shí)間戳類型除外)罢维,所以這篇筆記中分析handshake_message類型數(shù)據(jù)包的發(fā)送過程和接收過程淹仑。
運(yùn)行環(huán)境:CLion編譯器,并配置連接到主網(wǎng)節(jié)點(diǎn)肺孵。
1匀借、handshake_message消息結(jié)構(gòu)
struct handshake_message {
uint16_t network_version = 0; ///< incremental value above a computed base
chain_id_type chain_id; ///< used to identify chain
fc::sha256 node_id; ///< used to identify peers and prevent self-connect
chain::public_key_type key; ///< authentication key; may be a producer or peer key, or empty
tstamp time;
fc::sha256 token; ///< digest of time to prove we own the private key of the key above
chain::signature_type sig; ///< signature for the digest
string p2p_address;
uint32_t last_irreversible_block_num = 0;
block_id_type last_irreversible_block_id;
uint32_t head_num = 0;
block_id_type head_id;
string os;
string agent;
int16_t generation;
};
握手包內(nèi)容包括:網(wǎng)絡(luò)版本、chain_id平窘、node_id吓肋、p2p_address、節(jié)點(diǎn)名稱等配置信息瑰艘,以及鏈的狀態(tài)(當(dāng)前節(jié)點(diǎn)的不可逆區(qū)塊數(shù)是鬼、不可逆區(qū)塊id肤舞、最新區(qū)塊id、最新區(qū)塊數(shù))均蜜。其中區(qū)塊數(shù)是指區(qū)塊編號(hào)(1萨赁、2、3......)兆龙,區(qū)塊id是指32位的hash值杖爽。
2、發(fā)送handshake_message消息
啟動(dòng)本地節(jié)點(diǎn)之后紫皇,會(huì)連接到配置文件中的p2p節(jié)點(diǎn)慰安,并獲取當(dāng)前鏈信息、配置信息聪铺,然后構(gòu)建handshake_message包并發(fā)送到其他p2p節(jié)點(diǎn)化焕。
void connection::send_handshake( ) {
handshake_initializer::populate(last_handshake_sent); //填充消息內(nèi)容
last_handshake_sent.generation = ++sent_handshake_count;
fc_dlog(logger, "Sending handshake generation ${g} to ${ep}",
("g",last_handshake_sent.generation)("ep", peer_name()));
enqueue(last_handshake_sent); //構(gòu)建完成握手包之后,放入隊(duì)列中
}
發(fā)送的數(shù)據(jù)包內(nèi)容如下:
//大小 348個(gè)字節(jié) 連接之后發(fā)送的報(bào)文
0040 5c 01 00 00 00 b6 04 ac a3 76 f2 06 b8 fc 2?\....?.?£vò.?ü
0050 25 a6 ed 44 db dc 66 54 7c 36 c6 c3 3e 3a 11 9f %|íD?üfT|6??>:..
0060 fb ea ef 94 36 42 f0 e9 06 da b2 ea 2d 82 ce 71 ?ê?.6Beé.ú2ê-.?q
0070 45 c4 74 df 2f 3f 5f d9 df 11 af 8c 70 62 06 7a E?t?/?_ù?.ˉ.pb.z
0080 5d de 3e 6b 87 10 22 18 0c 00 00 00 00 00 00 00 ]T>k..".........
0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00a0 00 00 00 00 00 00 00 00 00 00 00 aa 8b b8 81 00 ...........a.?..
00b0 77 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 w...............
00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0110 00 00 00 00 00 2d 6d 6f 6f 6e 69 6e 77 61 74 65 .....-mooninwate
0120 72 64 65 4d 61 63 42 6f 6f 6b 2d 50 72 6f 2e 6c rdeMacBook-Pro.l
0130 6f 63 61 6c 3a 39 38 37 36 20 2d 20 64 61 62 32 ocal:9876 - dab2
0140 65 61 32 00 00 00 00 00 00 00 00 00 00 00 00 00 ea2.............
0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0160 00 00 00 00 00 00 00 01 00 00 00 00 00 00 01 40 ...............@
0170 51 47 47 7a b2 f5 f5 1c da 42 7b 63 81 91 c6 6d QGGz2??.úB{c..?m
0180 2c 59 aa 39 2d 5c 2c 98 07 6c b0 03 6f 73 78 10 ,Ya9-\,..l°.osx.
0190 22 45 4f 53 20 54 65 73 74 20 41 67 65 6e 74 22 "EOS Test Agent"
01a0 01 00
3铃剔、接收handshake_message消息
eos接收到其他節(jié)點(diǎn)發(fā)送過來的消息后撒桨,根據(jù)消息類型的不同,重載了不同的handle_message函數(shù)键兜,其中handshake_message類型凤类,主要功能包括兩方面:
(1)、驗(yàn)證接收到的handshake包內(nèi)容中的鏈id普气、節(jié)點(diǎn)id谜疤、網(wǎng)絡(luò)版本等信息。
(2)现诀、對(duì)比接收到的消息中鏈的狀態(tài)和自身鏈的狀態(tài)對(duì)比夷磕,然后進(jìn)行區(qū)塊同步。
//重載后的函數(shù)仔沿, 處理handshake_message消息
void net_plugin_impl::handle_message( connection_ptr c, const handshake_message &msg) {
peer_ilog(c, "received handshake_message");
if (!is_valid(msg)) {
peer_elog( c, "bad handshake message");
c->enqueue( go_away_message( fatal_other ));
return;
}
controller& cc = chain_plug->chain();
uint32_t lib_num = cc.last_irreversible_block_num( );
uint32_t peer_lib = msg.last_irreversible_block_num;
if (msg.generation == 1) {
if( c->peer_addr.empty() || c->last_handshake_recv.node_id == fc::sha256()) {
fc_dlog(logger, "checking for duplicate" );
//遍歷所有連接的節(jié)點(diǎn)坐桩,c代表當(dāng)前連接中的節(jié)點(diǎn) 保證同一個(gè)p2p節(jié)點(diǎn)只存在一個(gè)連接
········
}
}
if( msg.chain_id != chain_id) {
elog( "Peer on a different chain. Closing connection");
c->enqueue( go_away_message(go_away_reason::wrong_chain) );
return;
}
······
if( c->node_id != msg.node_id) {
c->node_id = msg.node_id;
}
········
c->last_handshake_recv = msg;
c->_logger_variant.reset();
sync_master->recv_handshake(c,msg); //根據(jù)鏈的狀態(tài)進(jìn)行同步管理
}
主要功能在于recv_handshake
函數(shù)中同步區(qū)塊管理。
void sync_manager::recv_handshake (connection_ptr c, const handshake_message &msg) {
controller& cc = chain_plug->chain();
uint32_t lib_num = cc.last_irreversible_block_num( );
uint32_t peer_lib = msg.last_irreversible_block_num;
reset_lib_num(c);
c->syncing = false;
//--------------------------------
// sync need checks; (lib == last irreversible block)
//
// 0. my head block id == peer head id means we are all caugnt up block wise
// 1. my head block num < peer lib - start sync locally
// 2. my lib > peer head num - send an last_irr_catch_up notice if not the first generation
//
// 3 my head block num <= peer head block num - update sync state and send a catchup request
// 4 my head block num > peer block num ssend a notice catchup if this is not the first generation
//
//-----------------------------
uint32_t head = cc.fork_db_head_block_num( );
block_id_type head_id = cc.fork_db_head_block_id();
if (head_id == msg.head_id) {
fc_dlog(logger, "sync check state 0");
// notify peer of our pending transactions
notice_message note;
note.known_blocks.mode = none;
note.known_trx.mode = catch_up;
note.known_trx.pending = my_impl->local_txns.size();
c->enqueue( note );
return;
}
if (head < peer_lib) {
fc_dlog(logger, "sync check state 1");
// wait for receipt of a notice message before initiating sync
if (c->protocol_version < proto_explicit_sync) {
start_sync( c, peer_lib);
}
return;
}
if (lib_num > msg.head_num ) {
fc_dlog(logger, "sync check state 2");
if (msg.generation > 1 || c->protocol_version > proto_base) {
notice_message note;
note.known_trx.pending = lib_num;
note.known_trx.mode = last_irr_catch_up;
note.known_blocks.mode = last_irr_catch_up;
note.known_blocks.pending = head;
c->enqueue( note );
}
c->syncing = true;
return;
}
if (head <= msg.head_num ) {
fc_dlog(logger, "sync check state 3");
verify_catchup (c, msg.head_num, msg.head_id);
return;
}
else {
fc_dlog(logger, "sync check state 4");
if (msg.generation > 1 || c->protocol_version > proto_base) {
notice_message note;
note.known_trx.mode = none;
note.known_blocks.mode = catch_up;
note.known_blocks.pending = head;
note.known_blocks.ids.push_back(head_id);
c->enqueue( note );
}
c->syncing = true;
return;
}
elog ("sync check failed to resolve status");
}
這里的對(duì)比區(qū)塊信息封锉,主要分為五種情況:
(1)绵跷、接收到的最新區(qū)塊id與自身最新區(qū)塊id相同。
(2)烘浦、自身最新區(qū)塊數(shù)小于接收到的區(qū)塊的不可逆數(shù)抖坪。(自己的最新區(qū)塊高度比遠(yuǎn)程節(jié)點(diǎn)的不可逆區(qū)塊高度還要低,需要同步闷叉。)
(3)擦俐、自身不可逆區(qū)塊數(shù)大于接收到的最新區(qū)塊數(shù)。(與(2)相反握侧,通知遠(yuǎn)程節(jié)點(diǎn)蚯瞧,需要同步嘿期。發(fā)送的是notice_message類型的消息,下一篇筆記中再寫埋合。)
(4)备徐、自身最新區(qū)塊數(shù)小于接收到的最新區(qū)塊數(shù)。(需要同步甚颂,發(fā)送的是request_message消息蜜猾,后面再寫這種消息類型。)
(5)振诬、自身最新區(qū)塊數(shù)大于接收到的最新區(qū)塊數(shù)蹭睡。(與(4)相反,告通知遠(yuǎn)程節(jié)點(diǎn)赶么,需要同步肩豁。發(fā)送的是notice_message類型的消息,下一篇筆記中在寫辫呻。)
以上便是handshake_message消息的發(fā)送與接收過程清钥。此篇筆記寫到本地節(jié)點(diǎn)將自己的配置信息、鏈的狀態(tài)告訴遠(yuǎn)程節(jié)點(diǎn)放闺,遠(yuǎn)程節(jié)點(diǎn)接收到這些信息之后祟昭,發(fā)現(xiàn)自身鏈比本地節(jié)點(diǎn)的鏈更長(zhǎng),所以給本地節(jié)點(diǎn)發(fā)送消息同步區(qū)塊雄人。(消息類型為notice_message从橘,下一篇分析本地節(jié)點(diǎn)接收到notice_message類型的消息之后,如何同步區(qū)塊)础钠。