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 _ => return Err(BucketError::UnknownCommand),
228 })
229 }
230
231 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
232 use LevinCommand as C;
233
234 match self {
235 Self::NewBlock(val) => build_message(C::NewBlock, val, builder)?,
236 Self::NewTransactions(val) => {
237 build_message(C::NewTransactions, val, builder)?;
238 }
239 Self::GetObjectsRequest(val) => {
240 build_message(C::GetObjectsRequest, val, builder)?;
241 }
242 Self::GetObjectsResponse(val) => {
243 build_message(C::GetObjectsResponse, val, builder)?;
244 }
245 Self::ChainRequest(val) => build_message(C::ChainRequest, val, builder)?,
246 Self::ChainEntryResponse(val) => {
247 build_message(C::ChainResponse, val, builder)?;
248 }
249 Self::NewFluffyBlock(val) => build_message(C::NewFluffyBlock, val, builder)?,
250 Self::FluffyMissingTransactionsRequest(val) => {
251 build_message(C::FluffyMissingTxsRequest, val, builder)?;
252 }
253 Self::GetTxPoolCompliment(val) => {
254 build_message(C::GetTxPoolCompliment, val, builder)?;
255 }
256 }
257 Ok(())
258 }
259}
260
261#[derive(Debug, Clone)]
262pub enum AdminRequestMessage {
263 Handshake(HandshakeRequest),
264 Ping,
265 SupportFlags,
266 TimedSync(TimedSyncRequest),
267}
268
269impl AdminRequestMessage {
270 pub const fn command(&self) -> LevinCommand {
271 use LevinCommand as C;
272
273 match self {
274 Self::Handshake(_) => C::Handshake,
275 Self::Ping => C::Ping,
276 Self::SupportFlags => C::SupportFlags,
277 Self::TimedSync(_) => C::TimedSync,
278 }
279 }
280
281 fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
282 use LevinCommand as C;
283
284 Ok(match command {
285 C::Handshake => decode_message(AdminRequestMessage::Handshake, buf)?,
286 C::TimedSync => decode_message(AdminRequestMessage::TimedSync, buf)?,
287 C::Ping => {
288 cuprate_epee_encoding::from_bytes::<EmptyMessage, _>(buf)
289 .map_err(|e| BucketError::BodyDecodingError(e.into()))?;
290
291 Self::Ping
292 }
293 C::SupportFlags => {
294 cuprate_epee_encoding::from_bytes::<EmptyMessage, _>(buf)
295 .map_err(|e| BucketError::BodyDecodingError(e.into()))?;
296
297 Self::SupportFlags
298 }
299 _ => return Err(BucketError::UnknownCommand),
300 })
301 }
302
303 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
304 use LevinCommand as C;
305
306 match self {
307 Self::Handshake(val) => build_message(C::Handshake, val, builder)?,
308 Self::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
309 Self::Ping => build_message(C::Ping, EmptyMessage, builder)?,
310 Self::SupportFlags => {
311 build_message(C::SupportFlags, EmptyMessage, builder)?;
312 }
313 }
314 Ok(())
315 }
316}
317
318#[derive(Debug, Clone)]
319pub enum AdminResponseMessage {
320 Handshake(HandshakeResponse),
321 Ping(PingResponse),
322 SupportFlags(SupportFlagsResponse),
323 TimedSync(TimedSyncResponse),
324}
325
326impl AdminResponseMessage {
327 pub const fn command(&self) -> LevinCommand {
328 use LevinCommand as C;
329
330 match self {
331 Self::Handshake(_) => C::Handshake,
332 Self::Ping(_) => C::Ping,
333 Self::SupportFlags(_) => C::SupportFlags,
334 Self::TimedSync(_) => C::TimedSync,
335 }
336 }
337
338 fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
339 use LevinCommand as C;
340
341 Ok(match command {
342 C::Handshake => decode_message(AdminResponseMessage::Handshake, buf)?,
343 C::TimedSync => decode_message(AdminResponseMessage::TimedSync, buf)?,
344 C::Ping => decode_message(AdminResponseMessage::Ping, buf)?,
345 C::SupportFlags => decode_message(AdminResponseMessage::SupportFlags, buf)?,
346 _ => return Err(BucketError::UnknownCommand),
347 })
348 }
349
350 fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
351 use LevinCommand as C;
352
353 match self {
354 Self::Handshake(val) => build_message(C::Handshake, val, builder)?,
355 Self::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
356 Self::Ping(val) => build_message(C::Ping, val, builder)?,
357 Self::SupportFlags(val) => {
358 build_message(C::SupportFlags, val, builder)?;
359 }
360 }
361 Ok(())
362 }
363}
364
365#[derive(Debug, Clone)]
366pub enum Message {
367 Request(AdminRequestMessage),
368 Response(AdminResponseMessage),
369 Protocol(ProtocolMessage),
370}
371
372impl Message {
373 pub const fn is_request(&self) -> bool {
374 matches!(self, Self::Request(_))
375 }
376
377 pub const fn is_response(&self) -> bool {
378 matches!(self, Self::Response(_))
379 }
380
381 pub const fn is_protocol(&self) -> bool {
382 matches!(self, Self::Protocol(_))
383 }
384
385 pub const fn command(&self) -> LevinCommand {
386 match self {
387 Self::Request(mes) => mes.command(),
388 Self::Response(mes) => mes.command(),
389 Self::Protocol(mes) => mes.command(),
390 }
391 }
392}
393
394impl LevinBody for Message {
395 type Command = LevinCommand;
396
397 fn decode_message<B: Buf>(
398 body: &mut B,
399 ty: MessageType,
400 command: LevinCommand,
401 ) -> Result<Self, BucketError> {
402 Ok(match ty {
403 MessageType::Request => Self::Request(AdminRequestMessage::decode(body, command)?),
404 MessageType::Response => Self::Response(AdminResponseMessage::decode(body, command)?),
405 MessageType::Notification => Self::Protocol(ProtocolMessage::decode(body, command)?),
406 })
407 }
408
409 fn encode(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
410 match self {
411 Self::Protocol(pro) => {
412 builder.set_message_type(MessageType::Notification);
413 builder.set_return_code(0);
414 pro.build(builder)
415 }
416 Self::Request(req) => {
417 builder.set_message_type(MessageType::Request);
418 builder.set_return_code(0);
419 req.build(builder)
420 }
421 Self::Response(res) => {
422 builder.set_message_type(MessageType::Response);
423 builder.set_return_code(1);
424 res.build(builder)
425 }
426 }
427 }
428}
429
430struct EmptyMessage;
435
436epee_object! {
437 EmptyMessage,
438}