ckb_light_client_protocol_server/
lib.rs1use 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
25pub struct LightClientProtocol {
27 pub shared: Shared,
29}
30
31impl LightClientProtocol {
32 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}