泰岳链使用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,
}

猜你喜欢

转载自blog.csdn.net/JIYILANZHOU/article/details/106862850