去年年初,很多人都說rust開發區塊鏈如何好,然後就學習了一下。最先接觸到的是substrate裏面的網絡模塊,當時對libp2p不是很瞭解,rust語法也一直半解,以致只能看懂應用消息的轉發流程,如何發到對方節點就不是很清楚,年初學習了下eth 2.0客戶端Lighthouse,裏面宏比較少結構相對清晰很多。
本文討論的節點同步有歷史塊同步、新區塊緩存,可參考以太坊download,fetcher模塊。不涉及節點發現,加密連接。
歷史塊同步
libp2p封裝性很好,提供了一系列模塊,如果只是簡單的發送區塊,通過Gossip可以很容易的做到。
如果新節點啓動去同步指定節點,發送請求消息
的時候,你發發現這個需要提供的模塊很難滿足你的要求,你需要實現自己的Behaviour
,ProtocolsHandler
,UpgradeInfo
,InboundUpgrade
,OutboundUpgrade
等一系列trait
。
impl<TSubstream> NetworkBehaviour for P2P<TSubstream>
where TSubstream: AsyncRead + AsyncWrite,
{
type ProtocolsHandler = P2PHandler<TSubstream>;
type OutEvent = P2PMessage;
fn new_handler(&mut self) -> Self::ProtocolsHandler {
P2PHandler::new(
SubstreamProtocol::new(P2PProtocol),
Duration::from_secs(30),
&self.log,
)
}
fn inject_connected(&mut self, peer_id: PeerId, connected_point: ConnectedPoint) {
self.events.push(NetworkBehaviourAction::GenerateEvent(
P2PMessage::InjectConnect(peer_id,connected_point),
));
}
fn inject_disconnected(&mut self, peer_id: &PeerId, _: ConnectedPoint) {
// inform the p2p handler that the peer has disconnected
self.events.push(NetworkBehaviourAction::GenerateEvent(
P2PMessage::PeerDisconnected(peer_id.clone()),
));
}
fn inject_node_event(&mut self, source: PeerId,
event: <Self::ProtocolsHandler as ProtocolsHandler>::OutEvent,
) {
// send the event to the user
self.events
.push(NetworkBehaviourAction::GenerateEvent(P2PMessage::P2P(
source, event,)));
}
fn poll(&mut self, _: &mut impl PollParameters,
) -> Async<
NetworkBehaviourAction<
<Self::ProtocolsHandler as ProtocolsHandler>::InEvent,
Self::OutEvent,>,
> {
if !self.events.is_empty() {
return Async::Ready(self.events.remove(0));
}
Async::NotReady
}
}
如何實現自定義協議內容有點多,打算放到後面講解。
握手消息
當節點建立連接後,節點需要交換本地的創世hash,區塊高度,網絡ID,判斷和對方是否在同一個區塊鏈網絡裏面。
#[derive(Serialize, Deserialize,Clone, Debug, PartialEq)]
pub struct StatusMessage {
/// genesis block hash
pub genesis_hash: Hash,
/// Latest finalized root.
pub finalized_root: Hash,
/// Latest finalized number.
pub finalized_number: u64,
/// The latest block root.
pub head_root: Hash,
/// The slot associated with the latest block root.
pub network_id: u16,
}
如果網絡id,創世hash都一致,對方的finalized_number
比你高,本地需要維護對方的狀態信息,然後啓動同步。
// add the peer to the head's pool
self.chains.target_head_slot = remote.finalized_number;
self.chains.target_head_root = remote.finalized_root;
self.chains.add_peer(network, peer_id);
let local = self.chain.read().unwrap().current_block().height();
self.chains.start_syncing(network, local);
批量區塊下載請求
由於當新節點加入網絡的時候,當前全網出塊高度已經很高了,你不可能一個一個的下載,需要一次下載多個區塊,這時候每個請求包需要攜帶起始高度,請求多少個塊,多少個塊回傳一次。
pub struct BlocksByRangeRequest {
/// The hash tree root of a block on the requested chain.
pub head_block_root: Hash,
/// The starting slot to request blocks.
pub start_slot: u64,
/// The number of blocks from the start slot.
pub count: u64,
/// The step increment to receive blocks.
///
/// A value of 1 returns every block.
/// A value of 2 returns every second block.
/// A value of 3 returns every third block and so on.
pub step: u64,
}