Skip to main content

amareleo_node_bft_events/
lib.rs

1// Copyright 2024 Aleo Network Foundation
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
18mod batch_certified;
19pub use batch_certified::BatchCertified;
20
21mod batch_propose;
22pub use batch_propose::BatchPropose;
23
24mod batch_signature;
25pub use batch_signature::BatchSignature;
26
27mod block_request;
28pub use block_request::BlockRequest;
29
30mod block_response;
31pub use block_response::{BlockResponse, DataBlocks};
32
33mod certificate_request;
34pub use certificate_request::CertificateRequest;
35
36mod certificate_response;
37pub use certificate_response::CertificateResponse;
38
39mod challenge_request;
40pub use challenge_request::ChallengeRequest;
41
42mod challenge_response;
43pub use challenge_response::ChallengeResponse;
44
45mod disconnect;
46pub use disconnect::{Disconnect, DisconnectReason};
47
48mod primary_ping;
49pub use primary_ping::PrimaryPing;
50
51mod transmission_request;
52pub use transmission_request::TransmissionRequest;
53
54mod transmission_response;
55pub use transmission_response::TransmissionResponse;
56
57mod validators_request;
58pub use validators_request::ValidatorsRequest;
59
60mod validators_response;
61pub use validators_response::ValidatorsResponse;
62
63mod worker_ping;
64pub use worker_ping::WorkerPing;
65
66use amareleo_node_sync::BlockLocators;
67use snarkvm::{
68    console::prelude::{FromBytes, Network, Read, ToBytes, Write, error},
69    ledger::{
70        block::Block,
71        narwhal::{BatchCertificate, BatchHeader, Data, Transmission, TransmissionID},
72    },
73    prelude::{Address, Field, Signature},
74};
75
76use anyhow::{Result, bail, ensure};
77use indexmap::{IndexMap, IndexSet};
78use serde::{Deserialize, Serialize};
79pub use std::io::{self, Result as IoResult};
80use std::{borrow::Cow, net::SocketAddr};
81
82pub trait EventTrait: ToBytes + FromBytes {
83    /// Returns the event name.
84    fn name(&self) -> Cow<'static, str>;
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
88// TODO (howardwu): For mainnet - Remove this clippy lint. The CertificateResponse should not
89//  be a large enum variant, after removing the versioning.
90#[allow(clippy::large_enum_variant)]
91pub enum Event<N: Network> {
92    BatchPropose(BatchPropose<N>),
93    BatchSignature(BatchSignature<N>),
94    BatchCertified(BatchCertified<N>),
95    BlockRequest(BlockRequest),
96    BlockResponse(BlockResponse<N>),
97    CertificateRequest(CertificateRequest<N>),
98    CertificateResponse(CertificateResponse<N>),
99    ChallengeRequest(ChallengeRequest<N>),
100    ChallengeResponse(ChallengeResponse<N>),
101    Disconnect(Disconnect),
102    PrimaryPing(PrimaryPing<N>),
103    TransmissionRequest(TransmissionRequest<N>),
104    TransmissionResponse(TransmissionResponse<N>),
105    ValidatorsRequest(ValidatorsRequest),
106    ValidatorsResponse(ValidatorsResponse<N>),
107    WorkerPing(WorkerPing<N>),
108}
109
110impl<N: Network> From<DisconnectReason> for Event<N> {
111    fn from(reason: DisconnectReason) -> Self {
112        Self::Disconnect(Disconnect { reason })
113    }
114}
115
116impl<N: Network> Event<N> {
117    /// The version of the event protocol; it can be incremented in order to force users to update.
118    pub const VERSION: u32 = 8;
119
120    /// Returns the event name.
121    #[inline]
122    pub fn name(&self) -> Cow<'static, str> {
123        match self {
124            Self::BatchPropose(event) => event.name(),
125            Self::BatchSignature(event) => event.name(),
126            Self::BatchCertified(event) => event.name(),
127            Self::BlockRequest(event) => event.name(),
128            Self::BlockResponse(event) => event.name(),
129            Self::CertificateRequest(event) => event.name(),
130            Self::CertificateResponse(event) => event.name(),
131            Self::ChallengeRequest(event) => event.name(),
132            Self::ChallengeResponse(event) => event.name(),
133            Self::Disconnect(event) => event.name(),
134            Self::PrimaryPing(event) => event.name(),
135            Self::TransmissionRequest(event) => event.name(),
136            Self::TransmissionResponse(event) => event.name(),
137            Self::ValidatorsRequest(event) => event.name(),
138            Self::ValidatorsResponse(event) => event.name(),
139            Self::WorkerPing(event) => event.name(),
140        }
141    }
142
143    /// Returns the event ID.
144    #[inline]
145    pub fn id(&self) -> u16 {
146        match self {
147            Self::BatchPropose(..) => 0,
148            Self::BatchSignature(..) => 1,
149            Self::BatchCertified(..) => 2,
150            Self::BlockRequest(..) => 3,
151            Self::BlockResponse(..) => 4,
152            Self::CertificateRequest(..) => 5,
153            Self::CertificateResponse(..) => 6,
154            Self::ChallengeRequest(..) => 7,
155            Self::ChallengeResponse(..) => 8,
156            Self::Disconnect(..) => 9,
157            Self::PrimaryPing(..) => 10,
158            Self::TransmissionRequest(..) => 11,
159            Self::TransmissionResponse(..) => 12,
160            Self::ValidatorsRequest(..) => 13,
161            Self::ValidatorsResponse(..) => 14,
162            Self::WorkerPing(..) => 15,
163        }
164    }
165}
166
167impl<N: Network> ToBytes for Event<N> {
168    fn write_le<W: io::Write>(&self, mut writer: W) -> IoResult<()> {
169        self.id().write_le(&mut writer)?;
170
171        match self {
172            Self::BatchPropose(event) => event.write_le(writer),
173            Self::BatchSignature(event) => event.write_le(writer),
174            Self::BatchCertified(event) => event.write_le(writer),
175            Self::BlockRequest(event) => event.write_le(writer),
176            Self::BlockResponse(event) => event.write_le(writer),
177            Self::CertificateRequest(event) => event.write_le(writer),
178            Self::CertificateResponse(event) => event.write_le(writer),
179            Self::ChallengeRequest(event) => event.write_le(writer),
180            Self::ChallengeResponse(event) => event.write_le(writer),
181            Self::Disconnect(event) => event.write_le(writer),
182            Self::PrimaryPing(event) => event.write_le(writer),
183            Self::TransmissionRequest(event) => event.write_le(writer),
184            Self::TransmissionResponse(event) => event.write_le(writer),
185            Self::ValidatorsRequest(event) => event.write_le(writer),
186            Self::ValidatorsResponse(event) => event.write_le(writer),
187            Self::WorkerPing(event) => event.write_le(writer),
188        }
189    }
190}
191
192impl<N: Network> FromBytes for Event<N> {
193    fn read_le<R: io::Read>(mut reader: R) -> io::Result<Self> {
194        // Read the event ID.
195        let id = u16::read_le(&mut reader).map_err(|_| error("Unknown event ID"))?;
196
197        // Deserialize the data field.
198        let event = match id {
199            0 => Self::BatchPropose(BatchPropose::read_le(&mut reader)?),
200            1 => Self::BatchSignature(BatchSignature::read_le(&mut reader)?),
201            2 => Self::BatchCertified(BatchCertified::read_le(&mut reader)?),
202            3 => Self::BlockRequest(BlockRequest::read_le(&mut reader)?),
203            4 => Self::BlockResponse(BlockResponse::read_le(&mut reader)?),
204            5 => Self::CertificateRequest(CertificateRequest::read_le(&mut reader)?),
205            6 => Self::CertificateResponse(CertificateResponse::read_le(&mut reader)?),
206            7 => Self::ChallengeRequest(ChallengeRequest::read_le(&mut reader)?),
207            8 => Self::ChallengeResponse(ChallengeResponse::read_le(&mut reader)?),
208            9 => Self::Disconnect(Disconnect::read_le(&mut reader)?),
209            10 => Self::PrimaryPing(PrimaryPing::read_le(&mut reader)?),
210            11 => Self::TransmissionRequest(TransmissionRequest::read_le(&mut reader)?),
211            12 => Self::TransmissionResponse(TransmissionResponse::read_le(&mut reader)?),
212            13 => Self::ValidatorsRequest(ValidatorsRequest::read_le(&mut reader)?),
213            14 => Self::ValidatorsResponse(ValidatorsResponse::read_le(&mut reader)?),
214            15 => Self::WorkerPing(WorkerPing::read_le(&mut reader)?),
215            16.. => return Err(error(format!("Unknown event ID {id}"))),
216        };
217
218        // Ensure that there are no "dangling" bytes.
219        if reader.bytes().next().is_some() {
220            return Err(error("Leftover bytes in an Event"));
221        }
222
223        Ok(event)
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use crate::Event;
230    use bytes::{Buf, BufMut, BytesMut};
231    use snarkvm::console::prelude::{FromBytes, ToBytes};
232    type CurrentNetwork = snarkvm::prelude::MainnetV0;
233
234    #[test]
235    fn deserializing_invalid_data_panics() {
236        let buf = BytesMut::default();
237        let invalid_id = u16::MAX;
238        invalid_id.write_le(&mut buf.clone().writer()).unwrap();
239        assert_eq!(
240            Event::<CurrentNetwork>::read_le(buf.reader()).unwrap_err().to_string(),
241            format!("Unknown event ID")
242        );
243    }
244}
245
246#[cfg(test)]
247pub mod prop_tests {
248    use crate::{
249        Disconnect,
250        DisconnectReason,
251        Event,
252        batch_certified::prop_tests::any_batch_certified,
253        batch_propose::prop_tests::any_batch_propose,
254        batch_signature::prop_tests::any_batch_signature,
255        certificate_request::prop_tests::any_certificate_request,
256        certificate_response::prop_tests::any_certificate_response,
257        challenge_request::prop_tests::any_challenge_request,
258        challenge_response::prop_tests::any_challenge_response,
259        transmission_request::prop_tests::any_transmission_request,
260        transmission_response::prop_tests::any_transmission_response,
261        worker_ping::prop_tests::any_worker_ping,
262    };
263    use snarkvm::{
264        console::{network::Network, types::Field},
265        ledger::{narwhal::TransmissionID, puzzle::SolutionID},
266        prelude::{FromBytes, Rng, ToBytes, Uniform},
267    };
268
269    use proptest::{
270        prelude::{BoxedStrategy, Just, Strategy, any},
271        prop_oneof,
272        sample::Selector,
273    };
274    use test_strategy::proptest;
275
276    type CurrentNetwork = snarkvm::prelude::MainnetV0;
277
278    /// Returns the current UTC epoch timestamp.
279    pub fn now() -> i64 {
280        time::OffsetDateTime::now_utc().unix_timestamp()
281    }
282
283    pub fn any_solution_id() -> BoxedStrategy<SolutionID<CurrentNetwork>> {
284        Just(0).prop_perturb(|_, mut rng| rng.gen::<u64>().into()).boxed()
285    }
286
287    pub fn any_transaction_id() -> BoxedStrategy<<CurrentNetwork as Network>::TransactionID> {
288        Just(0)
289            .prop_perturb(|_, mut rng| <CurrentNetwork as Network>::TransactionID::from(Field::rand(&mut rng)))
290            .boxed()
291    }
292
293    pub fn any_transmission_checksum() -> BoxedStrategy<<CurrentNetwork as Network>::TransmissionChecksum> {
294        Just(0).prop_perturb(|_, mut rng| rng.gen::<<CurrentNetwork as Network>::TransmissionChecksum>()).boxed()
295    }
296
297    pub fn any_transmission_id() -> BoxedStrategy<TransmissionID<CurrentNetwork>> {
298        prop_oneof![
299            (any_transaction_id(), any_transmission_checksum())
300                .prop_map(|(id, cs)| TransmissionID::Transaction(id, cs)),
301            (any_solution_id(), any_transmission_checksum()).prop_map(|(id, cs)| TransmissionID::Solution(id, cs)),
302        ]
303        .boxed()
304    }
305
306    pub fn any_event() -> BoxedStrategy<Event<CurrentNetwork>> {
307        prop_oneof![
308            any_batch_certified().prop_map(Event::BatchCertified),
309            any_batch_propose().prop_map(Event::BatchPropose),
310            any_batch_signature().prop_map(Event::BatchSignature),
311            any_certificate_request().prop_map(Event::CertificateRequest),
312            any_certificate_response().prop_map(Event::CertificateResponse),
313            any_challenge_request().prop_map(Event::ChallengeRequest),
314            any_challenge_response().prop_map(Event::ChallengeResponse),
315            (
316                Just(vec![
317                    DisconnectReason::ProtocolViolation,
318                    DisconnectReason::NoReasonGiven,
319                    DisconnectReason::InvalidChallengeResponse,
320                    DisconnectReason::OutdatedClientVersion,
321                ]),
322                any::<Selector>()
323            )
324                .prop_map(|(reasons, selector)| Event::Disconnect(Disconnect::from(selector.select(reasons)))),
325            any_transmission_request().prop_map(Event::TransmissionRequest),
326            any_transmission_response().prop_map(Event::TransmissionResponse),
327            any_worker_ping().prop_map(Event::WorkerPing)
328        ]
329        .boxed()
330    }
331
332    #[proptest]
333    fn serialize_deserialize(#[strategy(any_event())] original: Event<CurrentNetwork>) {
334        let mut buf = Vec::new();
335        Event::write_le(&original, &mut buf).unwrap();
336
337        let deserialized: Event<CurrentNetwork> = Event::read_le(&*buf).unwrap();
338        assert_eq!(original.id(), deserialized.id());
339        assert_eq!(original.name(), deserialized.name());
340    }
341}