use std::collections::HashMap;
use std::time::{Duration, Instant};
use bytes::Bytes;
use parking_lot::RwLock;
use crate::metainfo::{extract_layer_hashes, generate_proof_hashes, MerkleTree};
pub const HASH_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
pub const MAX_PENDING_HASH_REQUESTS: usize = 16;
#[derive(Debug, Clone)]
pub struct PendingHashRequest {
pub pieces_root: [u8; 32],
pub base_layer: u32,
pub index: u32,
pub length: u32,
pub proof_layers: u32,
pub sent_at: Instant,
}
impl PendingHashRequest {
pub fn new(
pieces_root: [u8; 32],
base_layer: u32,
index: u32,
length: u32,
proof_layers: u32,
) -> Self {
Self {
pieces_root,
base_layer,
index,
length,
proof_layers,
sent_at: Instant::now(),
}
}
pub fn is_expired(&self) -> bool {
self.sent_at.elapsed() > HASH_REQUEST_TIMEOUT
}
pub fn key(&self) -> HashRequestKey {
HashRequestKey {
pieces_root: self.pieces_root,
base_layer: self.base_layer,
index: self.index,
length: self.length,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HashRequestKey {
pub pieces_root: [u8; 32],
pub base_layer: u32,
pub index: u32,
pub length: u32,
}
#[derive(Debug, Clone)]
pub struct HashResponse {
pub pieces_root: [u8; 32],
pub base_layer: u32,
pub index: u32,
pub length: u32,
pub proof_layers: u32,
pub layer_hashes: Vec<[u8; 32]>,
pub proof_hashes: Vec<[u8; 32]>,
}
impl HashResponse {
pub fn from_raw(
pieces_root: [u8; 32],
base_layer: u32,
index: u32,
length: u32,
proof_layers: u32,
hashes: &Bytes,
) -> Option<Self> {
let expected_hash_count = (length + proof_layers) as usize;
if hashes.len() != expected_hash_count * 32 {
return None;
}
let mut layer_hashes = Vec::with_capacity(length as usize);
let mut proof_hashes = Vec::with_capacity(proof_layers as usize);
for (i, chunk) in hashes.chunks_exact(32).enumerate() {
let mut hash = [0u8; 32];
hash.copy_from_slice(chunk);
if i < length as usize {
layer_hashes.push(hash);
} else {
proof_hashes.push(hash);
}
}
Some(Self {
pieces_root,
base_layer,
index,
length,
proof_layers,
layer_hashes,
proof_hashes,
})
}
pub fn verify(&self, expected_root: &[u8; 32]) -> bool {
if self.layer_hashes.is_empty() {
return false;
}
let mut current_hashes = self.layer_hashes.clone();
let padded_len = current_hashes.len().next_power_of_two();
while current_hashes.len() < padded_len {
current_hashes.push([0u8; 32]);
}
while current_hashes.len() > 1 {
let mut next_level = Vec::with_capacity(current_hashes.len() / 2);
for chunk in current_hashes.chunks(2) {
next_level.push(hash_pair(&chunk[0], &chunk[1]));
}
current_hashes = next_level;
}
let mut subtree_root = current_hashes[0];
let mut position = (self.index / self.length) as usize;
for uncle in &self.proof_hashes {
let (left, right) = if position % 2 == 0 {
(subtree_root, *uncle)
} else {
(*uncle, subtree_root)
};
subtree_root = hash_pair(&left, &right);
position /= 2;
}
if self.proof_layers == 0 {
true
} else {
&subtree_root == expected_root
}
}
pub fn expected_proof_layers(&self, tree_depth: u32) -> u32 {
if self.length == 0 {
return 0;
}
let reduction_levels = (self.length as f64).log2().ceil() as u32;
let level_of_subtree = self.base_layer + reduction_levels;
tree_depth.saturating_sub(level_of_subtree)
}
}
fn hash_pair(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(left);
hasher.update(right);
hasher.finalize().into()
}
type ReceivedHashesMap = HashMap<[u8; 32], HashMap<u32, HashMap<u32, Vec<[u8; 32]>>>>;
pub struct HashRequestManager {
pending: RwLock<HashMap<HashRequestKey, PendingHashRequest>>,
received_hashes: RwLock<ReceivedHashesMap>,
}
impl HashRequestManager {
pub fn new() -> Self {
Self {
pending: RwLock::new(HashMap::new()),
received_hashes: RwLock::new(HashMap::new()),
}
}
pub fn add_request(&self, request: PendingHashRequest) -> bool {
let mut pending = self.pending.write();
if pending.len() >= MAX_PENDING_HASH_REQUESTS {
return false;
}
pending.insert(request.key(), request);
true
}
pub fn remove_request(
&self,
pieces_root: &[u8; 32],
base_layer: u32,
index: u32,
length: u32,
) -> Option<PendingHashRequest> {
let key = HashRequestKey {
pieces_root: *pieces_root,
base_layer,
index,
length,
};
self.pending.write().remove(&key)
}
pub fn pending_count(&self) -> usize {
self.pending.read().len()
}
pub fn remove_expired(&self) -> Vec<PendingHashRequest> {
let mut pending = self.pending.write();
let expired: Vec<_> = pending
.iter()
.filter(|(_, req)| req.is_expired())
.map(|(k, _)| *k)
.collect();
expired
.into_iter()
.filter_map(|k| pending.remove(&k))
.collect()
}
pub fn store_hashes(&self, response: &HashResponse) {
let mut received = self.received_hashes.write();
let file_hashes = received.entry(response.pieces_root).or_default();
let layer_hashes = file_hashes.entry(response.base_layer).or_default();
layer_hashes.insert(response.index, response.layer_hashes.clone());
}
pub fn get_hashes(
&self,
pieces_root: &[u8; 32],
base_layer: u32,
index: u32,
) -> Option<Vec<[u8; 32]>> {
let received = self.received_hashes.read();
received
.get(pieces_root)
.and_then(|f| f.get(&base_layer))
.and_then(|l| l.get(&index))
.cloned()
}
pub fn has_hashes(&self, pieces_root: &[u8; 32], base_layer: u32, index: u32) -> bool {
let received = self.received_hashes.read();
received
.get(pieces_root)
.and_then(|f| f.get(&base_layer))
.is_some_and(|l| l.contains_key(&index))
}
pub fn clear_file_hashes(&self, pieces_root: &[u8; 32]) {
self.received_hashes.write().remove(pieces_root);
}
pub fn clear(&self) {
self.pending.write().clear();
self.received_hashes.write().clear();
}
}
impl Default for HashRequestManager {
fn default() -> Self {
Self::new()
}
}
pub struct HashServer {
trees: RwLock<HashMap<[u8; 32], MerkleTree>>,
}
impl HashServer {
pub fn new() -> Self {
Self {
trees: RwLock::new(HashMap::new()),
}
}
pub fn register_tree(&self, pieces_root: [u8; 32], tree: MerkleTree) {
self.trees.write().insert(pieces_root, tree);
}
pub fn unregister_tree(&self, pieces_root: &[u8; 32]) {
self.trees.write().remove(pieces_root);
}
pub fn has_tree(&self, pieces_root: &[u8; 32]) -> bool {
self.trees.read().contains_key(pieces_root)
}
pub fn generate_response(
&self,
pieces_root: [u8; 32],
base_layer: u32,
index: u32,
length: u32,
proof_layers: u32,
) -> Option<Bytes> {
let trees = self.trees.read();
let tree = trees.get(&pieces_root)?;
let layer_hashes =
extract_layer_hashes(tree, base_layer as usize, index as usize, length as usize);
if layer_hashes.is_empty() {
return None;
}
let proof_hashes = generate_proof_hashes(
tree,
base_layer as usize,
index as usize,
length as usize,
proof_layers as usize,
);
let total_hashes = layer_hashes.len() + proof_hashes.len();
let mut data = Vec::with_capacity(total_hashes * 32);
for hash in &layer_hashes {
data.extend_from_slice(hash);
}
for hash in &proof_hashes {
data.extend_from_slice(hash);
}
Some(Bytes::from(data))
}
pub fn clear(&self) {
self.trees.write().clear();
}
}
impl Default for HashServer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pending_hash_request() {
let root = [0xABu8; 32];
let req = PendingHashRequest::new(root, 0, 0, 4, 2);
assert_eq!(req.pieces_root, root);
assert_eq!(req.base_layer, 0);
assert_eq!(req.index, 0);
assert_eq!(req.length, 4);
assert_eq!(req.proof_layers, 2);
assert!(!req.is_expired());
}
#[test]
fn test_hash_request_manager_add_remove() {
let manager = HashRequestManager::new();
let root = [0xCDu8; 32];
let req = PendingHashRequest::new(root, 0, 0, 4, 2);
assert!(manager.add_request(req.clone()));
assert_eq!(manager.pending_count(), 1);
let removed = manager.remove_request(&root, 0, 0, 4);
assert!(removed.is_some());
assert_eq!(manager.pending_count(), 0);
}
#[test]
fn test_hash_response_from_raw() {
let root = [0xEFu8; 32];
let mut hash_data = vec![0u8; 96];
hash_data[0..32].copy_from_slice(&[1u8; 32]);
hash_data[32..64].copy_from_slice(&[2u8; 32]);
hash_data[64..96].copy_from_slice(&[3u8; 32]);
let hashes = Bytes::from(hash_data);
let response = HashResponse::from_raw(root, 0, 0, 2, 1, &hashes).unwrap();
assert_eq!(response.layer_hashes.len(), 2);
assert_eq!(response.proof_hashes.len(), 1);
assert_eq!(response.layer_hashes[0], [1u8; 32]);
assert_eq!(response.layer_hashes[1], [2u8; 32]);
assert_eq!(response.proof_hashes[0], [3u8; 32]);
}
#[test]
fn test_hash_server_generate_response() {
let server = HashServer::new();
let leaves: Vec<[u8; 32]> = (0..4u8)
.map(|i| {
let mut h = [0u8; 32];
h[0] = i;
h
})
.collect();
let tree = MerkleTree::from_piece_hashes(leaves);
let root = tree.root().unwrap();
server.register_tree(root, tree);
assert!(server.has_tree(&root));
let response = server.generate_response(root, 0, 0, 2, 1);
assert!(response.is_some());
let data = response.unwrap();
assert_eq!(data.len(), 96);
}
#[test]
fn test_hash_request_manager_store_and_get() {
let manager = HashRequestManager::new();
let root = [0x12u8; 32];
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 2,
proof_layers: 0,
layer_hashes: vec![[1u8; 32], [2u8; 32]],
proof_hashes: vec![],
};
manager.store_hashes(&response);
let retrieved = manager.get_hashes(&root, 0, 0);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().len(), 2);
assert!(manager.has_hashes(&root, 0, 0));
assert!(!manager.has_hashes(&root, 0, 2));
}
#[test]
fn test_hash_response_verify_full_tree() {
let leaves: Vec<[u8; 32]> = (0..4u8)
.map(|i| {
let mut h = [0u8; 32];
h[0] = i;
h
})
.collect();
let tree = MerkleTree::from_piece_hashes(leaves.clone());
let root = tree.root().unwrap();
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 4,
proof_layers: 0,
layer_hashes: leaves.clone(),
proof_hashes: vec![],
};
assert!(response.verify(&root));
let uncle = hash_pair(&leaves[2], &leaves[3]);
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 2,
proof_layers: 1,
layer_hashes: vec![leaves[0], leaves[1]],
proof_hashes: vec![uncle],
};
assert!(response.verify(&root));
let uncle = hash_pair(&leaves[0], &leaves[1]);
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 2,
length: 2,
proof_layers: 1,
layer_hashes: vec![leaves[2], leaves[3]],
proof_hashes: vec![uncle],
};
assert!(response.verify(&root));
let wrong_uncle = [0xFFu8; 32];
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 2,
proof_layers: 1,
layer_hashes: vec![leaves[0], leaves[1]],
proof_hashes: vec![wrong_uncle],
};
assert!(!response.verify(&root));
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 0,
proof_layers: 0,
layer_hashes: vec![],
proof_hashes: vec![],
};
assert!(!response.verify(&root));
}
#[test]
fn test_hash_response_verify_larger_tree() {
let leaves: Vec<[u8; 32]> = (0..8u8)
.map(|i| {
let mut h = [0u8; 32];
h[0] = i;
h
})
.collect();
let tree = MerkleTree::from_piece_hashes(leaves.clone());
let root = tree.root().unwrap();
let h01 = hash_pair(&leaves[0], &leaves[1]);
let h23 = hash_pair(&leaves[2], &leaves[3]);
let h45 = hash_pair(&leaves[4], &leaves[5]);
let h67 = hash_pair(&leaves[6], &leaves[7]);
let h0123 = hash_pair(&h01, &h23);
let h4567 = hash_pair(&h45, &h67);
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 4,
proof_layers: 1,
layer_hashes: vec![leaves[0], leaves[1], leaves[2], leaves[3]],
proof_hashes: vec![h4567],
};
assert!(response.verify(&root));
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 2,
proof_layers: 2,
layer_hashes: vec![leaves[0], leaves[1]],
proof_hashes: vec![h23, h4567],
};
assert!(response.verify(&root));
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 4,
length: 2,
proof_layers: 2,
layer_hashes: vec![leaves[4], leaves[5]],
proof_hashes: vec![h67, h0123],
};
assert!(response.verify(&root));
}
#[test]
fn test_hash_response_expected_proof_layers() {
let root = [0u8; 32];
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 2,
proof_layers: 2,
layer_hashes: vec![[0u8; 32], [0u8; 32]],
proof_hashes: vec![],
};
assert_eq!(response.expected_proof_layers(3), 2);
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 4,
proof_layers: 1,
layer_hashes: vec![[0u8; 32]; 4],
proof_hashes: vec![],
};
assert_eq!(response.expected_proof_layers(3), 1);
let response = HashResponse {
pieces_root: root,
base_layer: 0,
index: 0,
length: 8,
proof_layers: 0,
layer_hashes: vec![[0u8; 32]; 8],
proof_hashes: vec![],
};
assert_eq!(response.expected_proof_layers(3), 0);
}
}