1use std::fmt::Formatter;
20
21use bytes::{Buf, BytesMut};
22
23use cuprate_epee_encoding::epee_object;
24use cuprate_levin::{
25 BucketBuilder, BucketError, LevinBody, LevinCommand as LevinCommandTrait, MessageType,
26};
27
28pub mod admin;
29pub mod common;
30pub mod protocol;
31
32use admin::*;
33pub use common::{BasicNodeData, CoreSyncData, PeerListEntryBase};
34use protocol::*;
35
36#[derive(Copy, Clone, Eq, PartialEq, Debug)]
37pub enum LevinCommand {
38 Handshake,
39 TimedSync,
40 Ping,
41 SupportFlags,
42
43 NewBlock,
44 NewTransactions,
45 GetObjectsRequest,
46 GetObjectsResponse,
47 ChainRequest,
48 ChainResponse,
49 NewFluffyBlock,
50 FluffyMissingTxsRequest,
51 GetTxPoolCompliment,
52
53 Unknown(u32),
54}
55
56impl std::fmt::Display for LevinCommand {
57 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58 if let Self::Unknown(id) = self {
59 return f.write_str(&format!("unknown id: {id}"));
60 }
61
62 f.write_str(match self {
63 Self::Handshake => "handshake",
64 Self::TimedSync => "timed sync",
65 Self::Ping => "ping",
66 Self::SupportFlags => "support flags",
67
68 Self::NewBlock => "new block",
69 Self::NewTransactions => "new transactions",
70 Self::GetObjectsRequest => "get objects request",
71 Self::GetObjectsResponse => "get objects response",
72 Self::ChainRequest => "chain request",
73 Self::ChainResponse => "chain response",
74 Self::NewFluffyBlock => "new fluffy block",
75 Self::FluffyMissingTxsRequest => "fluffy missing transaction request",
76 Self::GetTxPoolCompliment => "get transaction pool compliment",
77
78 Self::Unknown(_) => unreachable!(),
79 })
80 }
81}
82
83impl LevinCommandTrait for LevinCommand {
84 fn bucket_size_limit(&self) -> u64 {
85 #[expect(clippy::match_same_arms, reason = "formatting is more clear")]
87 match self {
88 Self::Handshake => 65536,
89 Self::TimedSync => 65536,
90 Self::Ping => 4096,
91 Self::SupportFlags => 4096,
92
93 Self::NewBlock => 1024 * 1024 * 128, Self::NewTransactions => 1024 * 1024 * 128, Self::GetObjectsRequest => 1024 * 1024 * 2, Self::GetObjectsResponse => 1024 * 1024 * 128, Self::ChainRequest => 512 * 1024, Self::ChainResponse => 1024 * 1024 * 4, Self::NewFluffyBlock => 1024 * 1024 * 4, Self::FluffyMissingTxsRequest => 1024 * 1024, Self::GetTxPoolCompliment => 1024 * 1024 * 4, Self::Unknown(_) => u64::MAX,
104 }
105 }
106
107 fn is_handshake(&self) -> bool {
108 matches!(self, Self::Handshake)
109 }
110}
111
112impl From<u32> for LevinCommand {
113 fn from(value: u32) -> Self {
114 match value {
115 1001 => Self::Handshake,
116 1002 => Self::TimedSync,
117 1003 => Self::Ping,
118 1007 => Self::SupportFlags,
119
120 2001 => Self::NewBlock,
121 2002 => Self::NewTransactions,
122 2003 => Self::GetObjectsRequest,
123 2004 => Self::GetObjectsResponse,
124 2006 => Self::ChainRequest,
125 2007 => Self::ChainResponse,
126 2008 => Self::NewFluffyBlock,
127 2009 => Self::FluffyMissingTxsRequest,
128 2010 => Self::GetTxPoolCompliment,
129
130 x => Self::Unknown(x),
131 }
132 }
133}
134
135impl From<LevinCommand> for u32 {
136 fn from(value: LevinCommand) -> Self {
137 match value {
138 LevinCommand::Handshake => 1001,
139 LevinCommand::TimedSync => 1002,
140 LevinCommand::Ping => 1003,
141 LevinCommand::SupportFlags => 1007,
142
143 LevinCommand::NewBlock => 2001,
144 LevinCommand::NewTransactions => 2002,
145 LevinCommand::GetObjectsRequest => 2003,
146 LevinCommand::GetObjectsResponse => 2004,
147 LevinCommand::ChainRequest => 2006,
148 LevinCommand::ChainResponse => 2007,
149 LevinCommand::NewFluffyBlock => 2008,
150 LevinCommand::FluffyMissingTxsRequest => 2009,
151 LevinCommand::GetTxPoolCompliment => 2010,
152
153 LevinCommand::Unknown(x) => x,
154 }
155 }
156}
157
158fn decode_message<B: Buf, T: cuprate_epee_encoding::EpeeObject, Ret>(
159 ret: impl FnOnce(T) -> Ret,
160 buf: &mut B,
161) -> Result<Ret, BucketError> {
162 let t = cuprate_epee_encoding::from_bytes(buf)
163 .map_err(|e| BucketError::BodyDecodingError(e.into()))?;
164 Ok(ret(t))
165}
166
167fn build_message<T: cuprate_epee_encoding::EpeeObject>(
168 id: LevinCommand,
169 val: T,
170 builder: &mut BucketBuilder<LevinCommand>,
171) -> Result<(), BucketError> {
172 builder.set_command(id);
173 builder.set_body(
174 cuprate_epee_encoding::to_bytes(val)
175 .map(BytesMut::freeze)
176 .map_err(|e| BucketError::BodyDecodingError(e.into()))?,
177 );
178 Ok(())
179}
180
181#[derive(Debug, Clone)]
182pub enum ProtocolMessage {
183 NewBlock(NewBlock),
184 NewFluffyBlock(NewFluffyBlock),
185 GetObjectsRequest(GetObjectsRequest),
186 GetObjectsResponse(GetObjectsResponse),
187 ChainRequest(ChainRequest),
188 ChainEntryResponse(ChainResponse),
189 NewTransactions(NewTransactions),
190 FluffyMissingTransactionsRequest(FluffyMissingTransactionsRequest),
191 GetTxPoolCompliment(GetTxPoolCompliment),
192}
193
194impl ProtocolMessage {
195 pub const fn command(&self) -> LevinCommand {
196 use LevinCommand as C;
197
198 match self {
199 Self::NewBlock(_) => C::NewBlock,
200 Self::NewFluffyBlock(_) => C::NewFluffyBlock,
201 Self::GetObjectsRequest(_) => C::GetObjectsRequest,
202 Self::GetObjectsResponse(_) => C::GetObjectsResponse,
203 Self::ChainRequest(_) => C::ChainRequest,
204 Self::ChainEntryResponse(_) => C::ChainResponse,
205 Self::NewTransactions(_) => C::NewTransactions,
206 Self::FluffyMissingTransactionsRequest(_) => C::FluffyMissingTxsRequest,
207 Self::GetTxPoolCompliment(_) => C::GetTxPoolCompliment,
208 }
209 }
210
211 fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
212 use LevinCommand as C;
213
214 Ok(match command {
215 C::NewBlock => decode_message(ProtocolMessage::NewBlock, buf)?,
216 C::NewTransactions => decode_message(ProtocolMessage::NewTransactions, buf)?,
217 C::GetObjectsRequest => decode_message(ProtocolMessage::GetObjectsRequest, buf)?,
218 C::GetObjectsResponse => decode_message(ProtocolMessage::GetObjectsResponse, buf)?,
219 C::ChainRequest => decode_message(ProtocolMessage::ChainRequest, buf)?,
220 C::ChainResponse => decode_message(ProtocolMessage::ChainEntryResponse, buf)?,
221 C::NewFluffyBlock => decode_message(ProtocolMessage::NewFluffyBlock, buf)?,
222 C::FluffyMissingTxsRequest => {
223 decode_message(ProtocolMessage::FluffyMissingTransactionsRequest, buf)?
224 }
225 C::GetTxPoolCompliment => decode_message(ProtocolMessage::GetTxPoolCompliment, buf)?,
226 _ => return Err(BucketError::UnknownCommand),
227 })
228 }
229
230 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
231 use LevinCommand as C;
232
233 match self {
234 Self::NewBlock(val) => build_message(C::NewBlock, val, builder)?,
235 Self::NewTransactions(val) => {
236 build_message(C::NewTransactions, val, builder)?;
237 }
238 Self::GetObjectsRequest(val) => {
239 build_message(C::GetObjectsRequest, val, builder)?;
240 }
241 Self::GetObjectsResponse(val) => {
242 build_message(C::GetObjectsResponse, val, builder)?;
243 }
244 Self::ChainRequest(val) => build_message(C::ChainRequest, val, builder)?,
245 Self::ChainEntryResponse(val) => {
246 build_message(C::ChainResponse, val, builder)?;
247 }
248 Self::NewFluffyBlock(val) => build_message(C::NewFluffyBlock, val, builder)?,
249 Self::FluffyMissingTransactionsRequest(val) => {
250 build_message(C::FluffyMissingTxsRequest, val, builder)?;
251 }
252 Self::GetTxPoolCompliment(val) => {
253 build_message(C::GetTxPoolCompliment, val, builder)?;
254 }
255 }
256 Ok(())
257 }
258}
259
260#[derive(Debug, Clone)]
261pub enum AdminRequestMessage {
262 Handshake(HandshakeRequest),
263 Ping,
264 SupportFlags,
265 TimedSync(TimedSyncRequest),
266}
267
268impl AdminRequestMessage {
269 pub const fn command(&self) -> LevinCommand {
270 use LevinCommand as C;
271
272 match self {
273 Self::Handshake(_) => C::Handshake,
274 Self::Ping => C::Ping,
275 Self::SupportFlags => C::SupportFlags,
276 Self::TimedSync(_) => C::TimedSync,
277 }
278 }
279
280 fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
281 use LevinCommand as C;
282
283 Ok(match command {
284 C::Handshake => decode_message(AdminRequestMessage::Handshake, buf)?,
285 C::TimedSync => decode_message(AdminRequestMessage::TimedSync, buf)?,
286 C::Ping => {
287 cuprate_epee_encoding::from_bytes::<EmptyMessage, _>(buf)
288 .map_err(|e| BucketError::BodyDecodingError(e.into()))?;
289
290 Self::Ping
291 }
292 C::SupportFlags => {
293 cuprate_epee_encoding::from_bytes::<EmptyMessage, _>(buf)
294 .map_err(|e| BucketError::BodyDecodingError(e.into()))?;
295
296 Self::SupportFlags
297 }
298 _ => return Err(BucketError::UnknownCommand),
299 })
300 }
301
302 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
303 use LevinCommand as C;
304
305 match self {
306 Self::Handshake(val) => build_message(C::Handshake, val, builder)?,
307 Self::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
308 Self::Ping => build_message(C::Ping, EmptyMessage, builder)?,
309 Self::SupportFlags => {
310 build_message(C::SupportFlags, EmptyMessage, builder)?;
311 }
312 }
313 Ok(())
314 }
315}
316
317#[derive(Debug, Clone)]
318pub enum AdminResponseMessage {
319 Handshake(HandshakeResponse),
320 Ping(PingResponse),
321 SupportFlags(SupportFlagsResponse),
322 TimedSync(TimedSyncResponse),
323}
324
325impl AdminResponseMessage {
326 pub const fn command(&self) -> LevinCommand {
327 use LevinCommand as C;
328
329 match self {
330 Self::Handshake(_) => C::Handshake,
331 Self::Ping(_) => C::Ping,
332 Self::SupportFlags(_) => C::SupportFlags,
333 Self::TimedSync(_) => C::TimedSync,
334 }
335 }
336
337 fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
338 use LevinCommand as C;
339
340 Ok(match command {
341 C::Handshake => decode_message(AdminResponseMessage::Handshake, buf)?,
342 C::TimedSync => decode_message(AdminResponseMessage::TimedSync, buf)?,
343 C::Ping => decode_message(AdminResponseMessage::Ping, buf)?,
344 C::SupportFlags => decode_message(AdminResponseMessage::SupportFlags, buf)?,
345 _ => return Err(BucketError::UnknownCommand),
346 })
347 }
348
349 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
350 use LevinCommand as C;
351
352 match self {
353 Self::Handshake(val) => build_message(C::Handshake, val, builder)?,
354 Self::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
355 Self::Ping(val) => build_message(C::Ping, val, builder)?,
356 Self::SupportFlags(val) => {
357 build_message(C::SupportFlags, val, builder)?;
358 }
359 }
360 Ok(())
361 }
362}
363
364#[derive(Debug, Clone)]
365pub enum Message {
366 Request(AdminRequestMessage),
367 Response(AdminResponseMessage),
368 Protocol(ProtocolMessage),
369}
370
371impl Message {
372 pub const fn is_request(&self) -> bool {
373 matches!(self, Self::Request(_))
374 }
375
376 pub const fn is_response(&self) -> bool {
377 matches!(self, Self::Response(_))
378 }
379
380 pub const fn is_protocol(&self) -> bool {
381 matches!(self, Self::Protocol(_))
382 }
383
384 pub const fn command(&self) -> LevinCommand {
385 match self {
386 Self::Request(mes) => mes.command(),
387 Self::Response(mes) => mes.command(),
388 Self::Protocol(mes) => mes.command(),
389 }
390 }
391}
392
393impl LevinBody for Message {
394 type Command = LevinCommand;
395
396 fn decode_message<B: Buf>(
397 body: &mut B,
398 ty: MessageType,
399 command: LevinCommand,
400 ) -> Result<Self, BucketError> {
401 Ok(match ty {
402 MessageType::Request => Self::Request(AdminRequestMessage::decode(body, command)?),
403 MessageType::Response => Self::Response(AdminResponseMessage::decode(body, command)?),
404 MessageType::Notification => Self::Protocol(ProtocolMessage::decode(body, command)?),
405 })
406 }
407
408 fn encode(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
409 match self {
410 Self::Protocol(pro) => {
411 builder.set_message_type(MessageType::Notification);
412 builder.set_return_code(0);
413 pro.build(builder)
414 }
415 Self::Request(req) => {
416 builder.set_message_type(MessageType::Request);
417 builder.set_return_code(0);
418 req.build(builder)
419 }
420 Self::Response(res) => {
421 builder.set_message_type(MessageType::Response);
422 builder.set_return_code(1);
423 res.build(builder)
424 }
425 }
426 }
427}
428
429struct EmptyMessage;
434
435epee_object! {
436 EmptyMessage,
437}