cs_mwc_libp2p_gossipsub/
topic.rs

1// Copyright 2020 Sigma Prime Pty Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use crate::rpc_proto;
22use base64::encode;
23use prost::Message;
24use sha2::{Digest, Sha256};
25use std::fmt;
26
27/// A generic trait that can be extended for various hashing types for a topic.
28pub trait Hasher {
29    /// The function that takes a topic string and creates a topic hash.
30    fn hash(topic_string: String) -> TopicHash;
31}
32
33/// A type for representing topics who use the identity hash.
34#[derive(Debug, Clone)]
35pub struct IdentityHash {}
36impl Hasher for IdentityHash {
37    /// Creates a [`TopicHash`] as a raw string.
38    fn hash(topic_string: String) -> TopicHash {
39        TopicHash { hash: topic_string }
40    }
41}
42
43#[derive(Debug, Clone)]
44pub struct Sha256Hash {}
45impl Hasher for Sha256Hash {
46    /// Creates a [`TopicHash`] by SHA256 hashing the topic then base64 encoding the
47    /// hash.
48    fn hash(topic_string: String) -> TopicHash {
49        let topic_descripter = rpc_proto::TopicDescriptor {
50            name: Some(topic_string),
51            auth: None,
52            enc: None,
53        };
54        let mut bytes = Vec::with_capacity(topic_descripter.encoded_len());
55        topic_descripter
56            .encode(&mut bytes)
57            .expect("buffer is large enough");
58        let hash = encode(Sha256::digest(&bytes).as_slice());
59        TopicHash { hash }
60    }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
64pub struct TopicHash {
65    /// The topic hash. Stored as a string to align with the protobuf API.
66    hash: String,
67}
68
69impl TopicHash {
70    pub fn from_raw(hash: impl Into<String>) -> TopicHash {
71        TopicHash { hash: hash.into() }
72    }
73
74    pub fn into_string(self) -> String {
75        self.hash
76    }
77
78    pub fn as_str(&self) -> &str {
79        &self.hash
80    }
81}
82
83/// A gossipsub topic.
84#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
85pub struct Topic<H: Hasher> {
86    topic: String,
87    phantom_data: std::marker::PhantomData<H>,
88}
89
90impl<H: Hasher> From<Topic<H>> for TopicHash {
91    fn from(topic: Topic<H>) -> TopicHash {
92        topic.hash()
93    }
94}
95
96impl<H: Hasher> Topic<H> {
97    pub fn new(topic: impl Into<String>) -> Self {
98        Topic {
99            topic: topic.into(),
100            phantom_data: std::marker::PhantomData,
101        }
102    }
103
104    pub fn hash(&self) -> TopicHash {
105        H::hash(self.topic.clone())
106    }
107}
108
109impl<H: Hasher> fmt::Display for Topic<H> {
110    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111        write!(f, "{}", self.topic)
112    }
113}
114
115impl fmt::Display for TopicHash {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(f, "{}", self.hash)
118    }
119}