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