sequencer/network/
cdn.rs

1/// Trait implementations for the CDN
2use std::marker::PhantomData;
3
4use bincode::Options;
5use cdn_broker::reexports::{
6    connection::protocols::{Quic, Tcp, TcpTls},
7    crypto::signature::{Serializable, SignatureScheme},
8    def::{hook::NoMessageHook, ConnectionDef, RunDef, Topic as TopicTrait},
9    discovery::{Embedded, Redis},
10};
11use hotshot::types::SignatureKey;
12use hotshot_types::{
13    traits::{network::Topic as HotShotTopic, node_implementation::NodeType},
14    utils::bincode_opts,
15};
16use num_enum::{IntoPrimitive, TryFromPrimitive};
17use static_assertions::const_assert_eq;
18
19/// The enum for the topics we can subscribe to in the Push CDN
20#[repr(u8)]
21#[derive(IntoPrimitive, TryFromPrimitive, Clone, PartialEq, Eq)]
22pub enum Topic {
23    /// The global topic
24    Global = 0,
25    /// The DA topic
26    Da = 1,
27}
28
29pub enum Namespace {}
30
31// Make sure the topics are the same as defined in `HotShot`.
32const_assert_eq!(Topic::Global as u8, HotShotTopic::Global as u8);
33const_assert_eq!(Topic::Da as u8, HotShotTopic::Da as u8);
34
35/// Implement the `TopicTrait` for our `Topic` enum. This lets us define
36/// compatible topics at the broker-level. Others will be rejected.
37impl TopicTrait for Topic {}
38
39/// A wrapped `SignatureKey`. We need to implement the Push CDN's `SignatureScheme`
40/// trait in order to sign and verify messages to/from the CDN.
41#[derive(Clone, Eq, PartialEq)]
42pub struct WrappedSignatureKey<T: SignatureKey + 'static>(pub T);
43impl<T: SignatureKey> SignatureScheme for WrappedSignatureKey<T> {
44    type PrivateKey = T::PrivateKey;
45    type PublicKey = Self;
46
47    /// Sign a message of arbitrary data and return the serialized signature
48    ///
49    /// The namespace is prefixed to the message before signing to prevent
50    /// signature replay attacks in different parts of the system.
51    fn sign(
52        private_key: &Self::PrivateKey,
53        namespace: &str,
54        message: &[u8],
55    ) -> anyhow::Result<Vec<u8>> {
56        // Combine the namespace and message into a single byte array
57        let message = [namespace.as_bytes(), message].concat();
58
59        let signature = T::sign(private_key, &message)?;
60        Ok(bincode_opts().serialize(&signature)?)
61    }
62
63    /// Verify a message of arbitrary data and return the result
64    ///
65    /// The namespace is prefixed to the message before verification to prevent
66    /// signature replay attacks in different parts of the system.
67    fn verify(
68        public_key: &Self::PublicKey,
69        namespace: &str,
70        message: &[u8],
71        signature: &[u8],
72    ) -> bool {
73        // Combine the namespace and message into a single byte array
74        let namespaced_message = [namespace.as_bytes(), message].concat();
75
76        let signature: T::PureAssembledSignatureType = match bincode_opts().deserialize(signature) {
77            Ok(key) => key,
78            Err(_) => return false,
79        };
80
81        public_key.0.validate(&signature, message)
82            || public_key.0.validate(&signature, &namespaced_message)
83    }
84}
85
86/// We need to implement the `Serializable` so the Push CDN can serialize the signatures
87/// and public keys and send them over the wire.
88impl<T: SignatureKey> Serializable for WrappedSignatureKey<T> {
89    fn serialize(&self) -> anyhow::Result<Vec<u8>> {
90        Ok(self.0.to_bytes())
91    }
92
93    fn deserialize(serialized: &[u8]) -> anyhow::Result<Self> {
94        Ok(WrappedSignatureKey(T::from_bytes(serialized)?))
95    }
96}
97
98/// The production run definition for the Push CDN.
99/// Uses the real protocols and a Redis discovery client.
100pub struct ProductionDef<TYPES: NodeType>(PhantomData<TYPES>);
101impl<TYPES: NodeType> RunDef for ProductionDef<TYPES> {
102    type User = UserDefQuic<TYPES>;
103    type User2 = UserDefTcp<TYPES>;
104    type Broker = BrokerDef<TYPES>;
105    type DiscoveryClientType = Redis;
106    type Topic = Topic;
107}
108
109/// The user definition for the Push CDN.
110/// Uses the Quic protocol and untrusted middleware.
111/// RM TODO: Remove this, switching to TCP+TLS singularly when everyone has updated
112pub struct UserDefQuic<TYPES: NodeType>(PhantomData<TYPES>);
113impl<TYPES: NodeType> ConnectionDef for UserDefQuic<TYPES> {
114    type Scheme = WrappedSignatureKey<TYPES::SignatureKey>;
115    type Protocol = Quic;
116    type MessageHook = NoMessageHook;
117}
118
119/// The (parallel, TCP) user definition for the Push CDN.
120/// Uses the TCP+TLS protocol and untrusted middleware.
121pub struct UserDefTcp<TYPES: NodeType>(PhantomData<TYPES>);
122impl<TYPES: NodeType> ConnectionDef for UserDefTcp<TYPES> {
123    type Scheme = WrappedSignatureKey<TYPES::SignatureKey>;
124    type Protocol = TcpTls;
125    type MessageHook = NoMessageHook;
126}
127
128/// The broker definition for the Push CDN.
129/// Uses the TCP protocol and trusted middleware.
130pub struct BrokerDef<TYPES: NodeType>(PhantomData<TYPES>);
131impl<TYPES: NodeType> ConnectionDef for BrokerDef<TYPES> {
132    type Scheme = WrappedSignatureKey<TYPES::SignatureKey>;
133    type Protocol = Tcp;
134    type MessageHook = NoMessageHook;
135}
136
137/// The client definition for the Push CDN. Uses the Quic
138/// protocol and no middleware. Differs from the user
139/// definition in that is on the client-side.
140#[derive(Clone)]
141pub struct ClientDef<TYPES: NodeType>(PhantomData<TYPES>);
142impl<TYPES: NodeType> ConnectionDef for ClientDef<TYPES> {
143    type Scheme = WrappedSignatureKey<TYPES::SignatureKey>;
144    type Protocol = Quic;
145    type MessageHook = NoMessageHook;
146}
147
148/// The testing run definition for the Push CDN.
149/// Uses the real protocols, but with an embedded discovery client.
150pub struct TestingDef<TYPES: NodeType>(PhantomData<TYPES>);
151impl<TYPES: NodeType> RunDef for TestingDef<TYPES> {
152    type User = UserDefQuic<TYPES>;
153    type User2 = UserDefTcp<TYPES>;
154    type Broker = BrokerDef<TYPES>;
155    type DiscoveryClientType = Embedded;
156    type Topic = Topic;
157}