Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/chaff-capture/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ impl TryFrom<Event> for Direction {
| Event::MachineBudgetReached
| Event::MachineBudgetRecovered
| Event::QueueEmpty(_)
| Event::SendBlocked => Err(CaptureError::CantConvert),
| Event::SendBlocked
| Event::BlockReleased => Err(CaptureError::CantConvert),
}
}
}
Expand Down
84 changes: 30 additions & 54 deletions crates/chaff-machines/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,60 @@

use std::time::Duration;

use chaff::{
action::{FrameworkAction, IntegratorAction},
distr::Distr,
event::Event,
machine,
machine::Machine,
};
use chaff::{action::IntegratorAction, distr::Distr, event::Event, machine, machine::Machine};

/// Construct the constant machine.
///
/// # Panics
///
/// Should not panic unless modified.
#[must_use]
#[expect(clippy::expect_used)]
#[expect(clippy::unwrap_used, clippy::expect_used)]
pub fn construct() -> Machine {
let hundred_ms: Distr = Duration::from_millis(100).try_into().expect("valid distr");
let long_delay: Distr = Duration::from_micros(u64::MAX)
.try_into()
.expect("valid distr");
let zero: Distr = Duration::ZERO.try_into().expect("valid distr");
let hundred_ms: Distr = Duration::from_millis(100).try_into().unwrap();

machine! {
queues: [Some(1), Some(1), Some(1)],
budget: Absolute(500),
state init {
action: IntegratorAction::BlockOutgoing(long_delay), // block immediately
transitions: [
Event::SendBlocked => schedule_release, // first blocked packet
],
},
state schedule_release {
// schedule a release with a 100ms delay
action: FrameworkAction::schedule(
IntegratorAction::ReleaseBlock,
0,
hundred_ms,
),
transitions: [
// since we scheduled on a 1 capacity queue, this transition should
// happen immediately
Event::QueueFilled(0) => blocked,
queues: [],
budget: Proportion(3.5),

state wait_clean {
actions: [
IntegratorAction::BlockOutgoing(hundred_ms),
],
},
state blocked {
transitions: [
Event::QueueEmpty(0) => send_decoy, // no real packet sent
Event::SendBlocked => wait, // real packet sent
Event::SendBlocked => wait_dirty,
Event::BlockReleased => send_decoy,
Event::MachineBudgetReached => end,
]
},
state wait {

state wait_dirty {
transitions: [
Event::QueueEmpty(0) => do_block, // wait until release happens
Event::BlockReleased => wait_clean,
Event::MachineBudgetReached => end,
]
},

state send_decoy {
action: FrameworkAction::schedule(
actions: [
IntegratorAction::SendDecoy,
2,
zero,
),
IntegratorAction::BlockOutgoing(hundred_ms),
],
transitions: [
// similar trick to schedule_release, this time used to do two actions
// in quick succession
Event::QueueFilled(2) => do_block,
Event::SendBlocked => wait_dirty,
Event::BlockReleased => send_decoy,
Event::MachineBudgetReached => end,
]
},
state do_block {
action: FrameworkAction::schedule(
IntegratorAction::BlockOutgoing(long_delay),
1,
zero,
),

state end {
actions: [
IntegratorAction::ReleaseBlock
],
transitions: [
// similar trick to send_decoy
Event::QueueFilled(1) => schedule_release,
Event::MachineBudgetRecovered => wait_clean,
],
},
}
}
.expect("preconstructed machine should be valid")
}
4 changes: 2 additions & 2 deletions crates/chaff-machines/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ pub fn construct_test_machine() -> Machine {

Machine::try_new(
vec![
State::new(Some(trans_probs), Some(IntegratorAction::SendDecoy), None),
State::new(None, Some(IntegratorAction::SendDecoy), None),
State::new(Some(trans_probs), [IntegratorAction::SendDecoy], None),
State::new(None, [IntegratorAction::SendDecoy], None),
],
[None],
None,
Expand Down
115 changes: 71 additions & 44 deletions crates/chaff-sim/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,27 @@ impl<R: Rng + CryptoRng> Simulator<R> {
let mut out_builder = TraceBuilder::default();
let base_instant = Instant::now();

let mut buffered_events = Vec::new();
while let Some(sim_now) = self.next_earliest_time(&block_state, base_instant) {
if block_state.until.is_some_and(|until| until <= sim_now) {
let block_released = if block_state.until.is_some_and(|until| until <= sim_now) {
self.queue.extend(block_state.release(sim_now));
}
true
} else {
false
};

let mut events_now = Vec::new();
while self.queue.peek_time().is_some_and(|t| t == sim_now) {
if let Some(event) = self.queue.pop() {
events_now.push(event);
if self.framework.is_initialised() {
while self.queue.peek_time().is_some_and(|t| t == sim_now) {
if let Some(event) = self.queue.pop() {
events_now.push(event);
}
}
}

let mut buffered_events = Vec::with_capacity(events_now.len());
if block_released {
buffered_events.push(Event::BlockReleased);
}
for event in &events_now {
if event.event == Event::SendNormal && block_state.is_active_at(event.time) {
block_state.buffer(event.clone());
Expand All @@ -330,6 +338,7 @@ impl<R: Rng + CryptoRng> Simulator<R> {

let sim_instant = base_instant + Duration::from_micros(sim_now);
let actions = self.framework.process(&buffered_events, sim_instant);
buffered_events.clear();

actions.into_iter().for_each(|action| match action {
IntegratorAction::SendDecoy => {
Expand All @@ -346,6 +355,7 @@ impl<R: Rng + CryptoRng> Simulator<R> {
}
IntegratorAction::ReleaseBlock => {
self.queue.extend(block_state.release(sim_now));
buffered_events.push(Event::BlockReleased);
}
});
}
Expand Down Expand Up @@ -480,17 +490,13 @@ mod tests {

let machine = Machine::try_new(
vec![
State::new(
Some(trans_0_to_1),
Some(IntegratorAction::ReleaseBlock),
None,
),
State::new(Some(trans_0_to_1), [IntegratorAction::ReleaseBlock], None),
State::new(
Some(trans_1_to_2),
Some(IntegratorAction::block_outgoing(long_delay)),
[IntegratorAction::block_outgoing(long_delay)],
None,
),
State::new(None, Some(IntegratorAction::ReleaseBlock), None),
State::new(None, [IntegratorAction::ReleaseBlock], None),
],
[],
None,
Expand Down Expand Up @@ -546,12 +552,8 @@ mod tests {

let machine = Machine::try_new(
vec![
State::new(Some(trans), Some(IntegratorAction::ReleaseBlock), None),
State::new(
None,
Some(IntegratorAction::block_outgoing(long_delay)),
None,
),
State::new(Some(trans), [IntegratorAction::ReleaseBlock], None),
State::new(None, [IntegratorAction::block_outgoing(long_delay)], None),
],
[],
None,
Expand Down Expand Up @@ -591,8 +593,8 @@ mod tests {

let machine = Machine::try_new(
vec![
State::new(Some(trans), Some(IntegratorAction::ReleaseBlock), None),
State::new(None, Some(IntegratorAction::SendDecoy), None),
State::new(Some(trans), [IntegratorAction::ReleaseBlock], None),
State::new(None, [IntegratorAction::SendDecoy], None),
],
[],
None,
Expand Down Expand Up @@ -638,12 +640,8 @@ mod tests {

let machine = Machine::try_new(
vec![
State::new(
Some(trans_0_to_1),
Some(IntegratorAction::ReleaseBlock),
None,
),
State::new(None, Some(IntegratorAction::BlockOutgoing(uniform)), None),
State::new(Some(trans_0_to_1), [IntegratorAction::ReleaseBlock], None),
State::new(None, [IntegratorAction::BlockOutgoing(uniform)], None),
],
[],
None,
Expand Down Expand Up @@ -704,12 +702,8 @@ mod tests {

let machine = Machine::try_new(
vec![
State::new(Some(trans), Some(IntegratorAction::ReleaseBlock), None),
State::new(
None,
Some(IntegratorAction::block_outgoing(long_delay)),
None,
),
State::new(Some(trans), [IntegratorAction::ReleaseBlock], None),
State::new(None, [IntegratorAction::block_outgoing(long_delay)], None),
],
[],
None,
Expand Down Expand Up @@ -744,7 +738,7 @@ mod tests {
let machine = machine! {
queues: [],
state dummy {
action: IntegratorAction::ReleaseBlock,
actions: [IntegratorAction::ReleaseBlock],
}
}
.unwrap();
Expand All @@ -771,11 +765,11 @@ mod tests {
let machine = machine! {
queues: [None],
state init {
action: FrameworkAction::schedule(
actions: [FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
DistrKind::Constant(delay_secs).try_into().unwrap()
),
)],
}
}
.unwrap();
Expand Down Expand Up @@ -814,11 +808,11 @@ mod tests {
let machine = machine! {
queues: [None],
state init {
action: FrameworkAction::schedule(
actions: [FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
DistrKind::Constant(0.0).try_into().unwrap()
),
)],
}
}
.unwrap();
Expand Down Expand Up @@ -856,11 +850,11 @@ mod tests {
let machine = machine! {
queues: [None],
state init {
action: FrameworkAction::schedule(
actions: [FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
DistrKind::Constant(1.0).try_into().unwrap()
),
)],
}
}
.unwrap();
Expand Down Expand Up @@ -896,11 +890,11 @@ mod tests {
let machine = machine! {
queues: [None; 2],
state init {
action: FrameworkAction::schedule(IntegratorAction::SendDecoy, 0, delay),
actions: [FrameworkAction::schedule(IntegratorAction::SendDecoy, 0, delay)],
transitions: [Event::ReceiveNormal => schedule_second]
},
state schedule_second {
action: FrameworkAction::schedule(IntegratorAction::SendDecoy, 1, delay),
actions: [FrameworkAction::schedule(IntegratorAction::SendDecoy, 1, delay)],
},
}
.unwrap();
Expand Down Expand Up @@ -940,11 +934,11 @@ mod tests {
let machine = machine! {
queues: [None],
state init {
action: FrameworkAction::schedule(
actions: [FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
DistrKind::Constant(1.0).try_into().unwrap()
),
)],
}
}
.unwrap();
Expand All @@ -963,4 +957,37 @@ mod tests {
// TODO: simulator is "perfect" in that it always jumps to the soonest event and an
// integrator is not in that they don't know the soonest event. need some way to simulate this
// and also the tests for it.

#[cfg(test)]
mod machines {
use super::Simulator;
use chaff::framework::Framework;
use chaff_capture::trace::{Direction, Trace};
use chaff_machines::constant;

#[test]
pub fn test_constant_machine() {
let machine = constant::construct();
let framework = Framework::new(machine, rand::rng());
let trace = Trace::new(
[Direction::Send; 7],
[0, 123, 100_000, 200_000, 104, 204_213, 1_000_000],
[0; 7],
);
let mut simulator = Simulator::with(framework, trace, rand::rng());
let (out, _) = simulator.run();

let deltas = out.timing_deltas();

assert!(
deltas
.iter()
.filter(|delta| **delta != 0 && **delta != 100_000)
.count()
== 0,
"found poorly spaced deltas: {deltas:?}",
);
assert!(out.len() < 35, "len: {}", out.len());
}
}
}
Loading
Loading