#distributed-systems #model-checking #paxos #raft #actor #actor-model #simulation

fibril_verifier

Verifies behavior for distributed systems implemented with the Fibril library

13 releases

0.0.12 Jun 5, 2023
0.0.11 Jun 4, 2023
0.0.10 Feb 12, 2023
0.0.8 Jan 17, 2023

#1113 in Concurrency

49 downloads per month

MIT/Apache

170KB
3.5K SLoC

Fibril Verifier

Fibril Verifier is a library for model checking Fibril systems.

Usage

Please see the fibril_verifier docs.


lib.rs:

Fibril Verifier is a library for model checking Fibril systems.

Example

[dependencies]
fibril = { version = "0", features = ["fibers", "rt", "serde_json"] }
serde = { version = "1", features = ["derive"] }

[dev-dependencies]
fibril_verifier = "0"
use fibril::*;

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
enum Msg { Inc, Double, Value(u64) }

fn server(sdk: Sdk<Msg>) {
    let mut val = 0;
    loop {
        let (src, msg) = sdk.recv();
        match msg {
            Msg::Inc => {
                val += 1;
                sdk.send(src, Msg::Value(val));
            }
            Msg::Double => {
                val *= 2;
                sdk.send(src, Msg::Value(val));
            }
            _ => sdk.exit(),
        }
    }
}

// In the binary, bind the server to a UDP socket.
fn run_server_over_udp() {
    use std::net::Ipv4Addr;
    let mut rt = UdpRuntime::new_with_serde_json()
        .ipv4(Ipv4Addr::LOCALHOST)
        .port_fn(|n| 3000 + n);
    rt.spawn(|| Fiber::new(server));
    rt.join().unwrap();
}

// In the tests, the model checker will attempt to find a "trace" (sequence of steps) that
// causes a panic.
#[cfg(test)]
#[test]
fn may_panic() {
    use fibril_verifier::*;
    let mut verifier = Verifier::new(|cfg| {
        let server = cfg.spawn(Fiber::new(server));
        cfg.spawn(Fiber::new(move |sdk| {
            sdk.send(server, Msg::Inc);
            let (_src, msg) = sdk.recv();
            assert_eq!(msg, Msg::Value(1)); // truth depends on race winner
        }));
        cfg.spawn(Fiber::new(move |sdk| {
            sdk.send(server, Msg::Double);
            let (_src, msg) = sdk.recv();
            assert_eq!(msg, Msg::Value(2)); // truth depends on race winner
        }));
    });
    // TIP: alternatively use verifier.assert_no_panic() to fail the test.
    let (msg, _minimal_trace) = verifier.assert_panic();
    assert!(msg.contains("left == right"));
}

And here is an example of how to interact with the server using netcat.

# send STDOUT to disk for clarity as replies are not newline delimited
$ nc -u 127.0.0.1 3000 > /tmp/replies
"Inc"
"Inc"
"Double"
^C

$ cat /tmp/replies
{"Value":1}{"Value":2}{"Value":4}

Dependencies

~4–13MB
~149K SLoC