use std::time::Duration;
use bincode::{DefaultOptions, Serializer};
use chrono::Utc;
use serde::Serialize;
use tokio::net::UdpSocket;
use super::super::Message;
use crate::clock::{Hlc, LogicalCounter, NodeId, PhysicalTime, Timestamp};
use crate::entry::{Entry, State};
use crate::{replicated_map::Config, ReplicatedMap};
use gossip::auth;
fn forged_update() -> Vec<u8> {
let far_future = Timestamp::new(
Hlc::new(PhysicalTime::from_millis(u64::MAX), LogicalCounter::new(0)),
NodeId::new(0),
);
let message = Message::Update::<i32, Entry<Timestamp, String>, State<String>>((
0,
Entry::present(far_future, "evil".to_string()),
));
let mut buf = Vec::new();
message
.serialize(&mut Serializer::new(&mut buf, DefaultOptions::new()))
.unwrap();
buf
}
#[tokio::test(flavor = "multi_thread")]
async fn forged_datagram_is_ignored() {
let key = [0x42u8; auth::KEY_LEN];
let port = 8082;
let victim_addr = "127.0.0.48";
let config = Config::default()
.with_port(port)
.with_listen_addr(victim_addr.parse().unwrap())
.with_cluster_key(auth::ClusterKey::new(key));
let store = ReplicatedMap::<i32, String>::new(config)
.await
.expect("bind failed");
store.just_insert(0, "legit".to_string());
let task = tokio::spawn(store.clone().run());
let attacker = UdpSocket::bind("127.0.0.49:0").await.unwrap();
let target = format!("{victim_addr}:{port}");
let forged = forged_update();
attacker.send_to(&forged, &target).await.unwrap();
let wrong_key_sealed =
auth::Authenticator::new(Some(auth::ClusterKey::new([0x99u8; auth::KEY_LEN])), false).seal(
gossip::replay::Seq::new(1),
gossip::replay::Stamp::new(Utc::now().timestamp_millis().max(0) as u64),
&forged,
);
attacker.send_to(&wrong_key_sealed, &target).await.unwrap();
tokio::time::sleep(Duration::from_millis(200)).await;
assert_eq!(store.get(&0).as_deref(), Some(&"legit".to_string()));
task.abort();
}