use crate::crypto::prelude::*;
use crate::crypto_random::Randomizable;
use crate::primitive::prelude::*;
#[cfg(feature = "rayon")]
use rayon::prelude::*;
use crate::internal::{
errors::{CoreTypesError, Result},
prelude::UnacknowledgedTicket,
};
pub const INTERMEDIATE_HOPS: usize = 3;
pub const DEFAULT_MINIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0;
pub const DEFAULT_MAXIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0;
pub type HoprPseudonym = SimplePseudonym;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Acknowledgement(
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE],
);
impl AsRef<[u8]> for Acknowledgement {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Acknowledgement {
type Error = GeneralError;
fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
Ok(Self(value.try_into().map_err(|_| {
GeneralError::ParseError("Acknowledgement".into())
})?))
}
}
impl BytesRepresentable for Acknowledgement {
const SIZE: usize = HalfKey::SIZE + OffchainSignature::SIZE;
}
pub const MIN_BATCH_SIZE: usize = 4;
impl Acknowledgement {
pub fn verify(self, sender_node_key: &OffchainPublicKey) -> Result<VerifiedAcknowledgement> {
let signature = OffchainSignature::try_from(
&self.0[HalfKey::SIZE..HalfKey::SIZE + OffchainSignature::SIZE],
)?;
if signature.verify_message(&self.0[0..HalfKey::SIZE], sender_node_key) {
Ok(VerifiedAcknowledgement {
ack_key_share: HalfKey::try_from(&self.0[0..HalfKey::SIZE])?,
signature,
})
} else {
Err(CoreTypesError::InvalidAcknowledgement)
}
}
pub fn verify_batch<I: IntoIterator<Item = (OffchainPublicKey, Self)>>(
acknowledgements: I,
) -> Vec<Result<VerifiedAcknowledgement>> {
let acknowledgements = acknowledgements.into_iter();
let (sz_lower_bound, _) = acknowledgements.size_hint();
if sz_lower_bound < MIN_BATCH_SIZE {
return acknowledgements
.into_iter()
.map(|(key, ack)| ack.verify(&key))
.collect();
}
let mut optimistic_result = Vec::with_capacity(sz_lower_bound);
let mut keys = Vec::with_capacity(sz_lower_bound);
let optimistic_check =
OffchainSignature::verify_batch(acknowledgements.filter_map(|(key, ack)| {
let maybe_ack_int =
HalfKey::try_from(&ack.0[0..HalfKey::SIZE]).and_then(|ack_key_share| {
OffchainSignature::try_from(
&ack.0[HalfKey::SIZE..HalfKey::SIZE + OffchainSignature::SIZE],
)
.map(|signature| (ack_key_share, signature))
});
match maybe_ack_int {
Ok((ack_key_share, signature)) => {
optimistic_result.push(Ok(VerifiedAcknowledgement {
ack_key_share,
signature,
}));
keys.push(Some(key)); Some(((ack_key_share, signature), key))
}
Err(err) => {
optimistic_result.push(Err(err.into()));
keys.push(None);
None
}
}
}));
if optimistic_check {
optimistic_result
} else {
#[cfg(feature = "rayon")]
let iter = (keys, optimistic_result).into_par_iter();
#[cfg(not(feature = "rayon"))]
let iter = keys.into_iter().zip(optimistic_result);
iter.filter_map(|(key, res)| key.zip(res.ok().map(VerifiedAcknowledgement::leak)))
.map(|(key, ack)| ack.verify(&key))
.collect()
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VerifiedAcknowledgement {
ack_key_share: HalfKey,
signature: OffchainSignature,
}
impl VerifiedAcknowledgement {
pub fn new(ack_key_share: HalfKey, node_keypair: &OffchainKeypair) -> Self {
let signature = OffchainSignature::sign_message(ack_key_share.as_ref(), node_keypair);
Self {
ack_key_share,
signature,
}
}
pub fn random(offchain_keypair: &OffchainKeypair) -> Self {
Self::new(HalfKey::random(), offchain_keypair)
}
pub fn leak(self) -> Acknowledgement {
let mut ret = [0u8; Acknowledgement::SIZE];
ret[0..HalfKey::SIZE].copy_from_slice(self.ack_key_share.as_ref());
ret[HalfKey::SIZE..HalfKey::SIZE + OffchainSignature::SIZE]
.copy_from_slice(self.signature.as_ref());
Acknowledgement(ret)
}
pub fn ack_key_share(&self) -> &HalfKey {
&self.ack_key_share
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PendingAcknowledgement {
WaitingAsSender,
WaitingAsRelayer(Box<UnacknowledgedTicket>),
}
#[cfg(test)]
mod tests {
use std::ops::Not;
use super::*;
#[test]
fn acknowledgement_should_verify() -> anyhow::Result<()> {
let kp = OffchainKeypair::random();
let v_ack_1 = VerifiedAcknowledgement::random(&kp);
let v_ack_2 = v_ack_1.leak().verify(kp.public())?;
assert_eq!(v_ack_1, v_ack_2);
Ok(())
}
#[parameterized::parameterized(batch_size = {1, 2, 100 })]
fn acknowledgement_should_verify_batch(batch_size: usize) -> anyhow::Result<()> {
let mut verified_acks = Vec::with_capacity(100);
let batch = (0..batch_size)
.map(|_| {
let kp = OffchainKeypair::random();
let ack = VerifiedAcknowledgement::random(&kp);
verified_acks.push(ack);
(*kp.public(), ack.leak())
})
.collect::<Vec<_>>();
let res = Acknowledgement::verify_batch(batch);
assert_eq!(batch_size, res.len());
assert!(res.iter().all(|r| r.is_ok()));
assert_eq!(
verified_acks,
res.into_iter().map(|r| r.unwrap()).collect::<Vec<_>>()
);
Ok(())
}
#[test]
fn acknowledgement_should_return_failed_ack_in_the_batch_verification() -> anyhow::Result<()> {
let mut verified_acks = Vec::with_capacity(100);
let batch = (0..100)
.map(|i| {
let kp = OffchainKeypair::random();
let ack = VerifiedAcknowledgement::random(&kp);
if i == 50 {
let mut non_verified = ack.leak();
non_verified.0[3] = non_verified.0[3].not();
(*kp.public(), non_verified)
} else {
verified_acks.push(ack);
(*kp.public(), ack.leak())
}
})
.collect::<Vec<_>>();
let res = Acknowledgement::verify_batch(batch);
assert_eq!(100, res.len());
assert!(res[50].is_err());
assert!(res.iter().enumerate().all(|(i, r)| r.is_ok() || i == 50));
assert_eq!(
verified_acks,
res.into_iter().filter_map(|r| r.ok()).collect::<Vec<_>>()
);
Ok(())
}
}