snarkos_node_router_messages/
lib.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![forbid(unsafe_code)]
17
18#[macro_use]
19extern crate tracing;
20
21pub mod helpers;
22pub use helpers::*;
23
24mod block_request;
25pub use block_request::BlockRequest;
26
27mod block_response;
28pub use block_response::BlockResponse;
29
30mod challenge_request;
31pub use challenge_request::ChallengeRequest;
32
33mod challenge_response;
34pub use challenge_response::ChallengeResponse;
35
36mod disconnect;
37pub use disconnect::Disconnect;
38
39mod peer_request;
40pub use peer_request::PeerRequest;
41
42mod peer_response;
43pub use peer_response::PeerResponse;
44
45mod ping;
46pub use ping::Ping;
47
48mod pong;
49pub use pong::Pong;
50
51mod puzzle_request;
52pub use puzzle_request::PuzzleRequest;
53
54mod puzzle_response;
55pub use puzzle_response::PuzzleResponse;
56
57mod unconfirmed_solution;
58pub use unconfirmed_solution::UnconfirmedSolution;
59
60mod unconfirmed_transaction;
61pub use unconfirmed_transaction::UnconfirmedTransaction;
62
63pub use snarkos_node_bft_events::DataBlocks;
64
65use snarkos_node_sync_locators::BlockLocators;
66use snarkvm::prelude::{
67    Address,
68    ConsensusVersion,
69    FromBytes,
70    Network,
71    Signature,
72    ToBytes,
73    block::{Header, Transaction},
74    error,
75    puzzle::{Solution, SolutionID},
76};
77
78use std::{
79    borrow::Cow,
80    fmt,
81    fmt::{Display, Formatter},
82    io,
83    net::SocketAddr,
84};
85
86pub trait MessageTrait: ToBytes + FromBytes {
87    /// Returns the message name.
88    fn name(&self) -> Cow<'static, str>;
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
92pub enum Message<N: Network> {
93    BlockRequest(BlockRequest),
94    BlockResponse(BlockResponse<N>),
95    ChallengeRequest(ChallengeRequest<N>),
96    ChallengeResponse(ChallengeResponse<N>),
97    Disconnect(Disconnect),
98    PeerRequest(PeerRequest),
99    PeerResponse(PeerResponse),
100    Ping(Ping<N>),
101    Pong(Pong),
102    PuzzleRequest(PuzzleRequest),
103    PuzzleResponse(PuzzleResponse<N>),
104    UnconfirmedSolution(UnconfirmedSolution<N>),
105    UnconfirmedTransaction(UnconfirmedTransaction<N>),
106}
107
108impl<N: Network> From<DisconnectReason> for Message<N> {
109    fn from(reason: DisconnectReason) -> Self {
110        Self::Disconnect(Disconnect { reason })
111    }
112}
113
114impl<N: Network> Message<N> {
115    /// The version of the network protocol; this is incremented for breaking changes between migration versions.
116    // Note. This should be incremented for each new `ConsensusVersion` that is added.
117    pub const VERSIONS: [(ConsensusVersion, u32); 6] = [
118        (ConsensusVersion::V5, 17),
119        (ConsensusVersion::V7, 18),
120        (ConsensusVersion::V8, 19),
121        (ConsensusVersion::V9, 20),
122        (ConsensusVersion::V10, 21),
123        (ConsensusVersion::V11, 22),
124    ];
125
126    /// Returns the latest message version.
127    pub fn latest_message_version() -> u32 {
128        Self::VERSIONS.last().map(|(_, version)| *version).unwrap_or(0)
129    }
130
131    /// Returns the lowest acceptable message version for the given block height.
132    /// Example scenario:
133    ///     At block height `X`, the protocol upgrades to message version from `Y-1` to `Y`.
134    ///     Client A upgrades and starts using message version `Y`.
135    ///     Client B has not upgraded and still uses message version `Y-1`.
136    ///     Until block `X`, they stay connected and can communicate.
137    ///     After block `X`, Client A will reject messages from Client B.
138    pub fn lowest_accepted_message_version(current_block_height: u32) -> u32 {
139        // Fetch the latest message version.
140        let latest_message_version = Self::latest_message_version();
141
142        // Fetch the versions.
143        let versions = Self::VERSIONS;
144
145        // Determine the minimum accepted message version.
146        N::CONSENSUS_VERSION(current_block_height).map_or(latest_message_version, |seek_version| {
147            // Search the consensus value for the specified version.
148            match versions.binary_search_by(|(version, _)| version.cmp(&seek_version)) {
149                // If a value was found for this consensus version, return it.
150                Ok(index) => versions[index].1,
151                // If the specified version was not found exactly, return the appropriate value belonging to the consensus version *lower* than the sought version.
152                // If the constant is not yet in effect at this consensus version, use the earliest version.
153                Err(index) => versions[index.saturating_sub(1)].1,
154            }
155        })
156    }
157
158    /// Returns the message name.
159    #[inline]
160    pub fn name(&self) -> Cow<'static, str> {
161        match self {
162            Self::BlockRequest(message) => message.name(),
163            Self::BlockResponse(message) => message.name(),
164            Self::ChallengeRequest(message) => message.name(),
165            Self::ChallengeResponse(message) => message.name(),
166            Self::Disconnect(message) => message.name(),
167            Self::PeerRequest(message) => message.name(),
168            Self::PeerResponse(message) => message.name(),
169            Self::Ping(message) => message.name(),
170            Self::Pong(message) => message.name(),
171            Self::PuzzleRequest(message) => message.name(),
172            Self::PuzzleResponse(message) => message.name(),
173            Self::UnconfirmedSolution(message) => message.name(),
174            Self::UnconfirmedTransaction(message) => message.name(),
175        }
176    }
177
178    /// Returns the message ID.
179    #[inline]
180    pub fn id(&self) -> u16 {
181        match self {
182            Self::BlockRequest(..) => 0,
183            Self::BlockResponse(..) => 1,
184            Self::ChallengeRequest(..) => 2,
185            Self::ChallengeResponse(..) => 3,
186            Self::Disconnect(..) => 4,
187            Self::PeerRequest(..) => 5,
188            Self::PeerResponse(..) => 6,
189            Self::Ping(..) => 7,
190            Self::Pong(..) => 8,
191            Self::PuzzleRequest(..) => 9,
192            Self::PuzzleResponse(..) => 10,
193            Self::UnconfirmedSolution(..) => 11,
194            Self::UnconfirmedTransaction(..) => 12,
195        }
196    }
197
198    /// Checks the message byte length. To be used before deserialization.
199    pub fn check_size(bytes: &[u8]) -> io::Result<()> {
200        // Store the length to be checked against the max message size for each variant.
201        let len = bytes.len();
202        if len < 2 {
203            return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid message"));
204        }
205
206        // Check the first two bytes for the message ID.
207        let id_bytes: [u8; 2] = (&bytes[..2])
208            .try_into()
209            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "id couldn't be deserialized"))?;
210        let id = u16::from_le_bytes(id_bytes);
211
212        // SPECIAL CASE: check the transaction message isn't too large.
213        if id == 12 && len > N::MAX_TRANSACTION_SIZE {
214            return Err(io::Error::new(io::ErrorKind::InvalidData, "transaction is too large"))?;
215        }
216
217        Ok(())
218    }
219}
220
221impl<N: Network> ToBytes for Message<N> {
222    fn write_le<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
223        self.id().write_le(&mut writer)?;
224
225        match self {
226            Self::BlockRequest(message) => message.write_le(writer),
227            Self::BlockResponse(message) => message.write_le(writer),
228            Self::ChallengeRequest(message) => message.write_le(writer),
229            Self::ChallengeResponse(message) => message.write_le(writer),
230            Self::Disconnect(message) => message.write_le(writer),
231            Self::PeerRequest(message) => message.write_le(writer),
232            Self::PeerResponse(message) => message.write_le(writer),
233            Self::Ping(message) => message.write_le(writer),
234            Self::Pong(message) => message.write_le(writer),
235            Self::PuzzleRequest(message) => message.write_le(writer),
236            Self::PuzzleResponse(message) => message.write_le(writer),
237            Self::UnconfirmedSolution(message) => message.write_le(writer),
238            Self::UnconfirmedTransaction(message) => message.write_le(writer),
239        }
240    }
241}
242
243impl<N: Network> FromBytes for Message<N> {
244    fn read_le<R: io::Read>(mut reader: R) -> io::Result<Self> {
245        // Read the event ID.
246        let mut id_bytes = [0u8; 2];
247        reader.read_exact(&mut id_bytes)?;
248        let id = u16::from_le_bytes(id_bytes);
249
250        // Deserialize the data field.
251        let message = match id {
252            0 => Self::BlockRequest(BlockRequest::read_le(&mut reader)?),
253            1 => Self::BlockResponse(BlockResponse::read_le(&mut reader)?),
254            2 => Self::ChallengeRequest(ChallengeRequest::read_le(&mut reader)?),
255            3 => Self::ChallengeResponse(ChallengeResponse::read_le(&mut reader)?),
256            4 => Self::Disconnect(Disconnect::read_le(&mut reader)?),
257            5 => Self::PeerRequest(PeerRequest::read_le(&mut reader)?),
258            6 => Self::PeerResponse(PeerResponse::read_le(&mut reader)?),
259            7 => Self::Ping(Ping::read_le(&mut reader)?),
260            8 => Self::Pong(Pong::read_le(&mut reader)?),
261            9 => Self::PuzzleRequest(PuzzleRequest::read_le(&mut reader)?),
262            10 => Self::PuzzleResponse(PuzzleResponse::read_le(&mut reader)?),
263            11 => Self::UnconfirmedSolution(UnconfirmedSolution::read_le(&mut reader)?),
264            12 => Self::UnconfirmedTransaction(UnconfirmedTransaction::read_le(&mut reader)?),
265            13.. => return Err(error("Unknown message ID {id}")),
266        };
267
268        // Ensure that there are no "dangling" bytes.
269        #[allow(clippy::unbuffered_bytes)]
270        if reader.bytes().next().is_some() {
271            return Err(error("Leftover bytes in a Message"));
272        }
273
274        Ok(message)
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0};
282
283    /// Ensure that the message versions used at genesis is correct.
284    fn consensus_constants_at_genesis<N: Network>() {
285        let height = 0;
286        let consensus_version = Message::<N>::lowest_accepted_message_version(height);
287        assert_eq!(consensus_version as usize, Message::<N>::VERSIONS.first().unwrap().1 as usize);
288    }
289
290    /// Ensure that the consensus *versions* are unique and incrementing.
291    fn consensus_versions<N: Network>() {
292        let mut previous_version = Message::<N>::VERSIONS.first().unwrap().0;
293        for (version, _) in Message::<N>::VERSIONS.iter().skip(1) {
294            assert!(*version as usize > previous_version as usize);
295            previous_version = *version;
296        }
297    }
298
299    /// Ensure that *message versions* are unique and incrementing by 1.
300    fn consensus_constants_increasing_heights<N: Network>() {
301        let mut previous_message_version = Message::<N>::VERSIONS.first().unwrap().1;
302        for (_, message_version) in Message::<N>::VERSIONS.iter().skip(1) {
303            assert_eq!(*message_version, previous_message_version + 1);
304            previous_message_version = *message_version;
305        }
306    }
307
308    #[test]
309    #[allow(clippy::assertions_on_constants)]
310    fn test_consensus_constants() {
311        consensus_constants_at_genesis::<MainnetV0>();
312        consensus_constants_at_genesis::<TestnetV0>();
313        consensus_constants_at_genesis::<CanaryV0>();
314
315        consensus_versions::<MainnetV0>();
316        consensus_versions::<TestnetV0>();
317        consensus_versions::<CanaryV0>();
318
319        consensus_constants_increasing_heights::<MainnetV0>();
320        consensus_constants_increasing_heights::<TestnetV0>();
321        consensus_constants_increasing_heights::<CanaryV0>();
322    }
323
324    #[test]
325    fn test_latest_consensus_version() {
326        let message_consensus_version = Message::<MainnetV0>::VERSIONS.last().unwrap().0;
327        let expected_consensus_version = MainnetV0::CONSENSUS_VERSION_HEIGHTS().last().unwrap().0;
328        assert_eq!(message_consensus_version, expected_consensus_version);
329
330        let message_consensus_version = Message::<TestnetV0>::VERSIONS.last().unwrap().0;
331        let expected_consensus_version = TestnetV0::CONSENSUS_VERSION_HEIGHTS().last().unwrap().0;
332        assert_eq!(message_consensus_version, expected_consensus_version);
333
334        let message_consensus_version = Message::<CanaryV0>::VERSIONS.last().unwrap().0;
335        let expected_consensus_version = CanaryV0::CONSENSUS_VERSION_HEIGHTS().last().unwrap().0;
336        assert_eq!(message_consensus_version, expected_consensus_version);
337    }
338}