use crate::crypto::{sha256_chunks, SHA256_OUTPUT_LEN};
use crate::lemire_index::buckets_for_two_thirds_load;
use crate::wire::{
DestinationHash, DestinationType, PacketType, WireAddress, WireContext, WireError,
TRUNCATED_HASH_BYTE_LEN,
};
pub const PACKET_HASH_LEN: usize = SHA256_OUTPUT_LEN;
pub const fn dedup_index_buckets(generation_capacity: usize) -> usize {
buckets_for_two_thirds_load(generation_capacity)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PacketHash([u8; PACKET_HASH_LEN]);
impl PacketHash {
pub const fn new(bytes: [u8; PACKET_HASH_LEN]) -> Self {
Self(bytes)
}
pub const fn as_bytes(&self) -> &[u8; PACKET_HASH_LEN] {
&self.0
}
pub fn of_wire_packet(bytes: &[u8]) -> Result<Self, WireError> {
const HASHED_FLAG_BITS: u8 = 0b0000_1111;
const TYPE_2_BIT: u8 = 0b0100_0000;
let flags = *bytes.first().ok_or(WireError::BufferTooShort)?;
let after_hops = 2;
let hashable_from = if flags & TYPE_2_BIT == TYPE_2_BIT {
after_hops + TRUNCATED_HASH_BYTE_LEN
} else {
after_hops
};
let tail = bytes
.get(hashable_from..)
.ok_or(WireError::BufferTooShort)?;
Ok(Self(sha256_chunks(&[&[flags & HASHED_FLAG_BITS], tail])))
}
pub fn proof_destination(&self) -> DestinationHash {
let mut bytes = [0u8; TRUNCATED_HASH_BYTE_LEN];
bytes.copy_from_slice(&self.0[..TRUNCATED_HASH_BYTE_LEN]);
DestinationHash::new(bytes)
}
pub fn of_data_fields(
destination_type: DestinationType,
address: &WireAddress,
context: WireContext,
payload: &[u8],
) -> Self {
Self::of_fields(
destination_type,
PacketType::Data,
address,
context,
payload,
)
}
pub fn of_fields(
destination_type: DestinationType,
packet_type: PacketType,
address: &WireAddress,
context: WireContext,
payload: &[u8],
) -> Self {
let hashed_flags = ((destination_type as u8) << 2) | (packet_type as u8);
Self(sha256_chunks(&[
&[hashed_flags],
address.as_bytes(),
&[context.to_byte()],
payload,
]))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RememberPacketOutcome {
AlreadyKnown,
StoredFresh,
StoredAfterRotation,
}
pub trait PacketHashHistory {
fn generation_capacity(&self) -> usize;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn contains(&self, hash: &PacketHash) -> bool;
fn remember(&mut self, hash: PacketHash) -> RememberPacketOutcome;
}
#[cfg(test)]
mod tests {
use super::*;
fn bytes_from_hex<const N: usize>(s: &str) -> [u8; N] {
let mut out = [0u8; N];
for (i, byte) in out.iter_mut().enumerate() {
*byte = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).expect("valid hex");
}
out
}
fn raw(hex: &str) -> std::vec::Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).expect("valid hex"))
.collect()
}
const RAW_PLAIN_DATA: &str = "080012f815e3e65add6ceb2fda0e7be338680068656c6c6f2d706c61696e";
const RAW_TYPE_2_DATA: &str =
"48004cd0cc45a7405dbd5cf9b5be1ef92f1012f815e3e65add6ceb2fda0e7be3386800ee";
#[test]
fn packet_hash_matches_the_rns_1_4_2_vectors() {
assert_eq!(
PacketHash::of_wire_packet(&raw(RAW_PLAIN_DATA)),
Ok(PacketHash::new(bytes_from_hex(
"2cab2ae2659f871fecf8f8da596c7f6369e5e8efcd9094d2f119dddec5704716"
))),
);
assert_eq!(
PacketHash::of_wire_packet(&raw(RAW_TYPE_2_DATA)),
Ok(PacketHash::new(bytes_from_hex(
"211f3da55c2c402e74645188c5e86fa9e2caaf0bde1a132ec8fd29eb4b38aa67"
))),
);
}
#[test]
fn field_wise_hashing_equals_wire_hashing() {
for packet in [RAW_PLAIN_DATA, RAW_TYPE_2_DATA] {
let bytes = raw(packet);
let from_wire = PacketHash::of_wire_packet(&bytes).unwrap();
let type_2 = bytes[0] & 0b0100_0000 != 0;
let destination_at = if type_2 { 18 } else { 2 };
let destination = crate::wire::DestinationHash::from_slice(
&bytes[destination_at..destination_at + 16],
)
.unwrap();
let destination_type = match (bytes[0] >> 2) & 0b11 {
0b00 => DestinationType::Single,
0b10 => DestinationType::Plain,
other => panic!("unexpected destination type bits {other:02b}"),
};
let payload = &bytes[destination_at + 17..];
assert_eq!(
PacketHash::of_data_fields(
destination_type,
&destination.to_address(),
WireContext::None,
payload,
),
from_wire,
);
}
}
#[test]
fn the_hops_count_never_changes_a_packets_hash() {
let mut relayed = raw(RAW_PLAIN_DATA);
relayed[1] = 77;
assert_eq!(
PacketHash::of_wire_packet(&relayed),
PacketHash::of_wire_packet(&raw(RAW_PLAIN_DATA)),
);
}
#[test]
fn the_transport_id_never_changes_a_packets_hash() {
let type_1 = raw(&format!(
"0800{}{}",
"12f815e3e65add6ceb2fda0e7be33868", "00ee"
));
let type_2 = raw(RAW_TYPE_2_DATA);
assert_eq!(
PacketHash::of_wire_packet(&type_1),
PacketHash::of_wire_packet(&type_2),
);
}
#[test]
fn unhashed_flag_bits_never_change_a_packets_hash() {
let baseline = PacketHash::of_wire_packet(&raw(RAW_PLAIN_DATA));
for high_bit in [0b1000_0000u8, 0b0010_0000, 0b0001_0000] {
let mut flipped = raw(RAW_PLAIN_DATA);
flipped[0] |= high_bit;
assert_eq!(PacketHash::of_wire_packet(&flipped), baseline);
}
}
#[test]
fn distinct_payloads_hash_distinctly() {
let mut other_payload = raw(RAW_PLAIN_DATA);
let last = other_payload.len() - 1;
other_payload[last] ^= 0xFF;
assert_ne!(
PacketHash::of_wire_packet(&other_payload),
PacketHash::of_wire_packet(&raw(RAW_PLAIN_DATA)),
);
}
#[test]
fn truncated_packets_are_unhashable() {
assert_eq!(
PacketHash::of_wire_packet(&[]),
Err(WireError::BufferTooShort),
);
let mut type_2_too_short = raw(RAW_TYPE_2_DATA);
type_2_too_short.truncate(10);
assert_eq!(
PacketHash::of_wire_packet(&type_2_too_short),
Err(WireError::BufferTooShort),
);
}
}