hopr_internal_types/
protocol.rs

1use hopr_crypto_random::Randomizable;
2use hopr_crypto_types::prelude::*;
3use hopr_primitive_types::prelude::*;
4use tracing::warn;
5
6use crate::{
7    errors::{CoreTypesError, Result},
8    prelude::UnacknowledgedTicket,
9};
10
11/// Number of intermediate hops: 3 relayers and 1 destination
12pub const INTERMEDIATE_HOPS: usize = 3;
13
14/// Default required minimum incoming ticket winning probability
15pub const DEFAULT_MINIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0;
16
17/// Default maximum incoming ticket winning probability, above which tickets will not be accepted
18/// due to privacy.
19pub const DEFAULT_MAXIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0; // TODO: change this in 3.0
20
21/// Alias for the [`Pseudonym`](`hopr_crypto_types::types::Pseudonym`) used in the HOPR protocol.
22pub type HoprPseudonym = SimplePseudonym;
23
24/// Represents packet acknowledgement
25#[derive(Copy, Clone, Debug, PartialEq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Acknowledgement {
28    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
29    data: [u8; Self::SIZE],
30    #[cfg_attr(feature = "serde", serde(skip))]
31    validated: bool,
32}
33
34impl AsRef<[u8]> for Acknowledgement {
35    fn as_ref(&self) -> &[u8] {
36        &self.data
37    }
38}
39
40impl TryFrom<&[u8]> for Acknowledgement {
41    type Error = GeneralError;
42
43    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
44        if value.len() == Self::SIZE {
45            Ok(Self {
46                data: value.try_into().unwrap(),
47                validated: false,
48            })
49        } else {
50            Err(GeneralError::ParseError("Acknowledgement".into()))
51        }
52    }
53}
54
55impl Acknowledgement {
56    pub fn new(ack_key_share: HalfKey, node_keypair: &OffchainKeypair) -> Self {
57        let signature = OffchainSignature::sign_message(ack_key_share.as_ref(), node_keypair);
58        let mut data = [0u8; Self::SIZE];
59        data[0..HalfKey::SIZE].copy_from_slice(ack_key_share.as_ref());
60        data[HalfKey::SIZE..HalfKey::SIZE + OffchainSignature::SIZE].copy_from_slice(signature.as_ref());
61
62        Self { data, validated: true }
63    }
64
65    /// Generates random but still a valid acknowledgement.
66    pub fn random(offchain_keypair: &OffchainKeypair) -> Self {
67        Self::new(HalfKey::random(), offchain_keypair)
68    }
69
70    /// Validates the acknowledgement.
71    ///
72    /// Must be called immediately after deserialization, or otherwise
73    /// any operations with the deserialized acknowledgement return an error.
74    #[tracing::instrument(level = "debug", skip(self, sender_node_key))]
75    pub fn validate(self, sender_node_key: &OffchainPublicKey) -> Result<Self> {
76        if !self.validated {
77            let signature =
78                OffchainSignature::try_from(&self.data[HalfKey::SIZE..HalfKey::SIZE + OffchainSignature::SIZE])?;
79            if signature.verify_message(&self.data[0..HalfKey::SIZE], sender_node_key) {
80                Ok(Self {
81                    data: self.data,
82                    validated: true,
83                })
84            } else {
85                Err(CoreTypesError::InvalidAcknowledgement)
86            }
87        } else {
88            Ok(self)
89        }
90    }
91
92    /// Gets the acknowledged key out of this acknowledgement.
93    ///
94    /// Returns [`InvalidAcknowledgement`]
95    /// if the acknowledgement has not been [validated](Acknowledgement::validate).
96    pub fn ack_key_share(&self) -> Result<HalfKey> {
97        if self.validated {
98            Ok(HalfKey::try_from(&self.data[0..HalfKey::SIZE])?)
99        } else {
100            Err(CoreTypesError::InvalidAcknowledgement)
101        }
102    }
103
104    /// Gets the acknowledgement challenge out of this acknowledgement.
105    ///
106    /// Returns [`InvalidAcknowledgement`]
107    /// if the acknowledgement has not been [validated](Acknowledgement::validate).
108    pub fn ack_challenge(&self) -> Result<HalfKeyChallenge> {
109        Ok(self.ack_key_share()?.to_challenge())
110    }
111
112    /// Indicates whether the acknowledgement has been [validated](Acknowledgement::validate).
113    pub fn is_validated(&self) -> bool {
114        self.validated
115    }
116}
117
118impl BytesRepresentable for Acknowledgement {
119    const SIZE: usize = HalfKey::SIZE + OffchainSignature::SIZE;
120}
121
122/// Contains either unacknowledged ticket if we're waiting for the acknowledgement as a relayer
123/// or information if we wait for the acknowledgement as a sender.
124#[allow(clippy::large_enum_variant)]
125#[derive(Clone, Debug, PartialEq, Eq)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127pub enum PendingAcknowledgement {
128    /// We're waiting for acknowledgement as a sender
129    WaitingAsSender,
130    /// We're waiting for the acknowledgement as a relayer with a ticket
131    WaitingAsRelayer(UnacknowledgedTicket),
132}