/ node / src / bootstrap_client / codec.rs
codec.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphaos library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use crate::{bft::events::Event, bootstrap_client::network::MessageOrEvent, router::messages::Message};
 20  use alphavm::prelude::{FromBytes, Network, ToBytes};
 21  
 22  use bytes::{BufMut, BytesMut};
 23  use core::marker::PhantomData;
 24  use tokio_util::codec::{Decoder, Encoder, LengthDelimitedCodec};
 25  
 26  /// The maximum size of a message that can be transmitted during the handshake.
 27  const MAX_HANDSHAKE_SIZE: usize = 1024 * 1024; // 1 MiB
 28  /// The maximum size of a post-handshake message that can be obtained from the network.
 29  const MAX_POST_HANDSHAKE_SIZE: usize = 2 * 1024 * 1024; // 2 MiB
 30  
 31  /// The codec used to decode and encode network messages.
 32  pub struct BootstrapClientCodec<N: Network> {
 33      codec: LengthDelimitedCodec,
 34      _phantom: PhantomData<N>,
 35  }
 36  
 37  impl<N: Network> BootstrapClientCodec<N> {
 38      pub fn handshake() -> Self {
 39          let mut codec = Self::default();
 40          codec.codec.set_max_frame_length(MAX_HANDSHAKE_SIZE);
 41          codec
 42      }
 43  }
 44  
 45  impl<N: Network> Default for BootstrapClientCodec<N> {
 46      fn default() -> Self {
 47          Self {
 48              codec: LengthDelimitedCodec::builder()
 49                  .max_frame_length(MAX_POST_HANDSHAKE_SIZE)
 50                  .little_endian()
 51                  .new_codec(),
 52              _phantom: Default::default(),
 53          }
 54      }
 55  }
 56  
 57  impl<N: Network> Encoder<Message<N>> for BootstrapClientCodec<N> {
 58      type Error = std::io::Error;
 59  
 60      fn encode(&mut self, message: Message<N>, dst: &mut BytesMut) -> Result<(), Self::Error> {
 61          // Serialize the payload directly into dst.
 62          message
 63              .write_le(&mut dst.writer())
 64              // This error should never happen, the conversion is for greater compatibility.
 65              .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "serialization error"))?;
 66  
 67          let serialized_message = dst.split_to(dst.len()).freeze();
 68  
 69          self.codec.encode(serialized_message, dst)
 70      }
 71  }
 72  
 73  impl<N: Network> Encoder<Event<N>> for BootstrapClientCodec<N> {
 74      type Error = std::io::Error;
 75  
 76      fn encode(&mut self, event: Event<N>, dst: &mut BytesMut) -> Result<(), Self::Error> {
 77          // Serialize the payload directly into dst.
 78          event
 79              .write_le(&mut dst.writer())
 80              // This error should never happen, the conversion is for greater compatibility.
 81              .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "serialization error"))?;
 82  
 83          let serialized_event = dst.split_to(dst.len()).freeze();
 84  
 85          self.codec.encode(serialized_event, dst)
 86      }
 87  }
 88  
 89  impl<N: Network> Encoder<MessageOrEvent<N>> for BootstrapClientCodec<N> {
 90      type Error = std::io::Error;
 91  
 92      fn encode(&mut self, item: MessageOrEvent<N>, dst: &mut BytesMut) -> Result<(), Self::Error> {
 93          // Serialize the payload directly into dst.
 94          match item {
 95              MessageOrEvent::Message(message) => self.encode(message, dst),
 96              MessageOrEvent::Event(event) => self.encode(event, dst),
 97          }
 98      }
 99  }
100  
101  impl<N: Network> Decoder for BootstrapClientCodec<N> {
102      type Error = std::io::Error;
103      type Item = MessageOrEvent<N>;
104  
105      fn decode(&mut self, source: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
106          // Decode a frame containing bytes belonging to a message.
107          let bytes = match self.codec.decode(source)? {
108              Some(bytes) => bytes,
109              None => return Ok(None),
110          };
111  
112          // Reject invalid/truncated messages.
113          if bytes.len() < 2 {
114              warn!("Failed to deserialize a message: too short");
115              return Err(std::io::ErrorKind::InvalidData.into());
116          }
117  
118          // Check the ID of the serialized Message or Event.
119          let message_id = u16::from_le_bytes(bytes[..2].try_into().unwrap());
120  
121          // Discard messages that aren't of interest to a bootstrapper node.
122          match message_id {
123              2..=5 => match Message::read_le(&bytes[..]) {
124                  Ok(message) => Ok(Some(MessageOrEvent::Message(message))),
125                  Err(error) => {
126                      warn!("Failed to deserialize a message: {error}");
127                      Err(std::io::ErrorKind::InvalidData.into())
128                  }
129              },
130              7..=9 | 13 => match Event::read_le(&bytes[..]) {
131                  Ok(event) => Ok(Some(MessageOrEvent::Event(event))),
132                  Err(error) => {
133                      warn!("Failed to deserialize a message: {error}");
134                      Err(std::io::ErrorKind::InvalidData.into())
135                  }
136              },
137              id => {
138                  trace!("Ignoring an unhandled message (ID {id})");
139                  Ok(None)
140              }
141          }
142      }
143  }