ckb_light_client_protocol_server/
lib.rs

1//! Server-side implementation for CKB light client protocol.
2//!
3//! TODO(light-client) More documentation.
4
5use std::sync::Arc;
6
7use ckb_logger::{debug, error, info, trace, warn};
8use ckb_network::{CKBProtocolContext, CKBProtocolHandler, PeerIndex, async_trait, bytes::Bytes};
9use ckb_shared::Shared;
10use ckb_store::ChainStore;
11use ckb_types::{core, packed, prelude::*};
12
13use crate::prelude::*;
14
15mod components;
16mod constant;
17mod prelude;
18mod status;
19
20#[cfg(test)]
21mod tests;
22
23pub use status::{Status, StatusCode};
24
25/// Light client protocol handler.
26pub struct LightClientProtocol {
27    /// Sync shared state.
28    pub shared: Shared,
29}
30
31impl LightClientProtocol {
32    /// Create a new light client protocol handler.
33    pub fn new(shared: Shared) -> Self {
34        Self { shared }
35    }
36}
37
38#[async_trait]
39impl CKBProtocolHandler for LightClientProtocol {
40    async fn init(&mut self, _nc: Arc<dyn CKBProtocolContext + Sync>) {}
41
42    async fn connected(
43        &mut self,
44        _nc: Arc<dyn CKBProtocolContext + Sync>,
45        peer: PeerIndex,
46        version: &str,
47    ) {
48        info!("LightClient({}).connected peer={}", version, peer);
49    }
50
51    async fn disconnected(&mut self, _nc: Arc<dyn CKBProtocolContext + Sync>, peer: PeerIndex) {
52        info!("LightClient.disconnected peer={}", peer);
53    }
54
55    async fn received(
56        &mut self,
57        nc: Arc<dyn CKBProtocolContext + Sync>,
58        peer: PeerIndex,
59        data: Bytes,
60    ) {
61        trace!("LightClient.received peer={}", peer);
62
63        let msg = match packed::LightClientMessageReader::from_slice(&data) {
64            Ok(msg) => msg.to_enum(),
65            _ => {
66                warn!(
67                    "LightClient.received a malformed message from Peer({})",
68                    peer
69                );
70                nc.ban_peer(
71                    peer,
72                    constant::BAD_MESSAGE_BAN_TIME,
73                    String::from("send us a malformed message"),
74                );
75                return;
76            }
77        };
78
79        let item_name = msg.item_name();
80        let status = self.try_process(nc.as_ref(), peer, msg);
81        if let Some(ban_time) = status.should_ban() {
82            error!(
83                "process {} from {}; ban {:?} since result is {}",
84                item_name, peer, ban_time, status
85            );
86            nc.ban_peer(peer, ban_time, status.to_string());
87        } else if status.should_warn() {
88            warn!("process {} from {}; result is {}", item_name, peer, status);
89        } else if !status.is_ok() {
90            debug!("process {} from {}; result is {}", item_name, peer, status);
91        }
92    }
93}
94
95impl LightClientProtocol {
96    fn try_process(
97        &mut self,
98        nc: &dyn CKBProtocolContext,
99        peer_index: PeerIndex,
100        message: packed::LightClientMessageUnionReader<'_>,
101    ) -> Status {
102        match message {
103            packed::LightClientMessageUnionReader::GetLastState(reader) => {
104                components::GetLastStateProcess::new(reader, self, peer_index, nc).execute()
105            }
106            packed::LightClientMessageUnionReader::GetLastStateProof(reader) => {
107                components::GetLastStateProofProcess::new(reader, self, peer_index, nc).execute()
108            }
109            packed::LightClientMessageUnionReader::GetBlocksProof(reader) => {
110                components::GetBlocksProofProcess::new(reader, self, peer_index, nc).execute()
111            }
112            packed::LightClientMessageUnionReader::GetTransactionsProof(reader) => {
113                components::GetTransactionsProofProcess::new(reader, self, peer_index, nc).execute()
114            }
115            _ => StatusCode::UnexpectedProtocolMessage.into(),
116        }
117    }
118
119    pub(crate) fn get_verifiable_tip_header(&self) -> Result<packed::VerifiableHeader, String> {
120        let snapshot = self.shared.snapshot();
121
122        let tip_hash = snapshot.tip_hash();
123        let tip_block = snapshot
124            .get_block(&tip_hash)
125            .expect("checked: tip block should be existed");
126        let parent_chain_root = if tip_block.is_genesis() {
127            Default::default()
128        } else {
129            let mmr = snapshot.chain_root_mmr(tip_block.number() - 1);
130            match mmr.get_root() {
131                Ok(root) => root,
132                Err(err) => {
133                    let errmsg = format!("failed to generate a root since {err:?}");
134                    return Err(errmsg);
135                }
136            }
137        };
138
139        let tip_header = packed::VerifiableHeader::new_builder()
140            .header(tip_block.header().data())
141            .uncles_hash(tip_block.calc_uncles_hash())
142            .extension(Pack::pack(&tip_block.extension()))
143            .parent_chain_root(parent_chain_root)
144            .build();
145
146        Ok(tip_header)
147    }
148
149    pub(crate) fn reply_tip_state<T>(&self, peer: PeerIndex, nc: &dyn CKBProtocolContext) -> Status
150    where
151        T: Entity,
152        <T as Entity>::Builder: ProverMessageBuilder,
153        <<T as Entity>::Builder as Builder>::Entity: Into<packed::LightClientMessageUnion>,
154    {
155        let tip_header = match self.get_verifiable_tip_header() {
156            Ok(tip_state) => tip_state,
157            Err(errmsg) => {
158                return StatusCode::InternalError.with_context(errmsg);
159            }
160        };
161        let content = T::new_builder().set_last_header(tip_header).build();
162        let message = packed::LightClientMessage::new_builder()
163            .set(content)
164            .build();
165        nc.reply(peer, &message);
166        Status::ok()
167    }
168
169    pub(crate) fn reply_proof<T>(
170        &self,
171        peer: PeerIndex,
172        nc: &dyn CKBProtocolContext,
173        last_block: &core::BlockView,
174        items_positions: Vec<u64>,
175        proved_items: <<T as Entity>::Builder as ProverMessageBuilder>::ProvedItems,
176        missing_items: <<T as Entity>::Builder as ProverMessageBuilder>::MissingItems,
177    ) -> Status
178    where
179        T: Entity,
180        <T as Entity>::Builder: ProverMessageBuilder,
181        <<T as Entity>::Builder as Builder>::Entity: Into<packed::LightClientMessageUnion>,
182    {
183        let (parent_chain_root, proof) = {
184            let snapshot = self.shared.snapshot();
185            let mmr = snapshot.chain_root_mmr(last_block.number() - 1);
186            let parent_chain_root = match mmr.get_root() {
187                Ok(root) => root,
188                Err(err) => {
189                    let errmsg = format!("failed to generate a root since {err:?}");
190                    return StatusCode::InternalError.with_context(errmsg);
191                }
192            };
193            let proof = if items_positions.is_empty() {
194                Default::default()
195            } else {
196                match mmr.gen_proof(items_positions) {
197                    Ok(proof) => proof.proof_items().to_owned(),
198                    Err(err) => {
199                        let errmsg = format!("failed to generate a proof since {err:?}");
200                        return StatusCode::InternalError.with_context(errmsg);
201                    }
202                }
203            };
204            (parent_chain_root, proof)
205        };
206        let verifiable_last_header = packed::VerifiableHeader::new_builder()
207            .header(last_block.data().header())
208            .uncles_hash(last_block.calc_uncles_hash())
209            .extension(Pack::pack(&last_block.extension()))
210            .parent_chain_root(parent_chain_root)
211            .build();
212        let content = T::new_builder()
213            .set_last_header(verifiable_last_header)
214            .set_proof(proof.into())
215            .set_proved_items(proved_items)
216            .set_missing_items(missing_items)
217            .build();
218        let message = packed::LightClientMessage::new_builder()
219            .set(content)
220            .build();
221        nc.reply(peer, &message);
222        Status::ok()
223    }
224}