use std::marker::PhantomData;
use delegate::delegate;
use hotshot_types::traits::signature_key::SignatureKey;
use libp2p::kad::store::{Error, RecordStore, Result};
use tracing::warn;
use crate::network::behaviours::dht::record::{RecordKey, RecordValue};
pub struct ValidatedStore<R: RecordStore, K: SignatureKey> {
store: R,
phantom: std::marker::PhantomData<K>,
}
impl<R: RecordStore, K: SignatureKey> ValidatedStore<R, K> {
pub fn new(store: R) -> Self {
ValidatedStore {
store,
phantom: PhantomData,
}
}
}
impl<R: RecordStore, K: SignatureKey> RecordStore for ValidatedStore<R, K>
where
K: 'static,
{
type ProvidedIter<'a>
= R::ProvidedIter<'a>
where
R: 'a,
K: 'a;
type RecordsIter<'a>
= R::RecordsIter<'a>
where
R: 'a,
K: 'a;
delegate! {
to self.store{
fn add_provider(&mut self, record: libp2p::kad::ProviderRecord) -> libp2p::kad::store::Result<()>;
fn get(&self, k: &libp2p::kad::RecordKey) -> Option<std::borrow::Cow<'_, libp2p::kad::Record>>;
fn provided(&self) -> Self::ProvidedIter<'_>;
fn providers(&self, key: &libp2p::kad::RecordKey) -> Vec<libp2p::kad::ProviderRecord>;
fn records(&self) -> Self::RecordsIter<'_>;
fn remove(&mut self, k: &libp2p::kad::RecordKey);
fn remove_provider(&mut self, k: &libp2p::kad::RecordKey, p: &libp2p::PeerId);
}
}
fn put(&mut self, record: libp2p::kad::Record) -> Result<()> {
if let Ok(record_value) = RecordValue::<K>::try_from(record.clone()) {
let Ok(record_key) = RecordKey::try_from_bytes(&record.key.to_vec()) else {
warn!("Failed to convert record key");
return Err(Error::MaxRecords);
};
if record_value.validate(&record_key) {
if let Err(err) = self.store.put(record.clone()) {
warn!("Failed to store record: {:?}", err);
return Err(Error::MaxRecords);
}
} else {
warn!("Failed to validate record");
return Err(Error::MaxRecords);
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use hotshot_types::signature_key::BLSPubKey;
use libp2p::{
kad::{store::MemoryStore, Record},
PeerId,
};
use super::*;
use crate::network::behaviours::dht::record::Namespace;
#[test]
fn test_valid_stored() {
let (public_key, private_key) = BLSPubKey::generated_from_seed_indexed([1; 32], 1337);
let value = vec![5, 6, 7, 8];
let record_key = RecordKey::new(Namespace::Lookup, public_key.to_bytes());
let record_value: RecordValue<BLSPubKey> =
RecordValue::new_signed(&record_key, value.clone(), &private_key).unwrap();
let mut store: ValidatedStore<MemoryStore, BLSPubKey> =
ValidatedStore::new(MemoryStore::new(PeerId::random()));
let record_value_bytes =
bincode::serialize(&record_value).expect("Failed to serialize record value");
let record = Record::new(record_key.to_bytes(), record_value_bytes);
store.put(record).expect("Failed to store record");
let libp2p_record_key = libp2p::kad::RecordKey::new(&record_key.to_bytes());
let stored_record = store.get(&libp2p_record_key).expect("Failed to get record");
let stored_record_value: RecordValue<BLSPubKey> =
bincode::deserialize(&stored_record.value).expect("Failed to deserialize record value");
assert_eq!(
record_value, stored_record_value,
"Stored record is not the same as original"
);
}
#[test]
fn test_invalid_not_stored() {
let (public_key, _) = BLSPubKey::generated_from_seed_indexed([1; 32], 1337);
let record_key = RecordKey::new(Namespace::Lookup, public_key.to_bytes());
let record_value: RecordValue<BLSPubKey> = RecordValue::new(vec![2, 3]);
let mut store: ValidatedStore<MemoryStore, BLSPubKey> =
ValidatedStore::new(MemoryStore::new(PeerId::random()));
let record_value_bytes =
bincode::serialize(&record_value).expect("Failed to serialize record value");
let record = Record::new(record_key.to_bytes(), record_value_bytes);
assert!(store.put(record).is_err(), "Should not have stored record");
let libp2p_record_key = libp2p::kad::RecordKey::new(&record_key.to_bytes());
assert!(
store.get(&libp2p_record_key).is_none(),
"Should not have stored record"
);
}
}