泰嶽鏈使用rust-libp2p實現節點同步(一)

去年年初,很多人都說rust開發區塊鏈如何好,然後就學習了一下。最先接觸到的是substrate裏面的網絡模塊,當時對libp2p不是很瞭解,rust語法也一直半解,以致只能看懂應用消息的轉發流程,如何發到對方節點就不是很清楚,年初學習了下eth 2.0客戶端Lighthouse,裏面宏比較少結構相對清晰很多。

本文討論的節點同步有歷史塊同步、新區塊緩存,可參考以太坊download,fetcher模塊。不涉及節點發現,加密連接。

歷史塊同步

libp2p封裝性很好,提供了一系列模塊,如果只是簡單的發送區塊,通過Gossip可以很容易的做到。
如果新節點啓動去同步指定節點,發送請求消息的時候,你發發現這個需要提供的模塊很難滿足你的要求,你需要實現自己的Behaviour,ProtocolsHandler,UpgradeInfoInboundUpgrade,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,
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章