Execute Alice and Bob state machines concurrently

Previously we were testing the protocol by manually driving Alice and
Bob's state machines. This logic has now be moved to an async state
transition function that can take any possible state as input. The
state transition function is called in a loop until it returns the
desired state. This allows use to interrupt midway through the protocol
and perform refund and punish tests. This design was chosen over a
generator based implementation because the the generator based
implementation results in a impure state transition function that is
difficult to reason about and prone to bugs.

Test related code was extracted into the tests folder.

The 2b and 4b states were renamed to be consistent with the rest.

Macros were used to reduce code duplication when converting
child states to their parent states and vice versa.

Todos were added were neccessary.
This commit is contained in:
rishflab 2020-09-29 15:36:50 +10:00
parent 3d3864807d
commit 8754a9931b
15 changed files with 1211 additions and 503 deletions

92
xmr-btc/tests/node.rs Normal file
View file

@ -0,0 +1,92 @@
use crate::{transport::Transport, wallet};
use anyhow::Result;
use rand::{CryptoRng, RngCore};
use xmr_btc::{alice, bob};
// TODO: merge this with bob node
// This struct is responsible for I/O
pub struct AliceNode<'a> {
transport: Transport<alice::Message, bob::Message>,
pub bitcoin_wallet: wallet::bitcoin::Wallet,
pub monero_wallet: wallet::monero::AliceWallet<'a>,
}
impl<'a> AliceNode<'a> {
pub fn new(
transport: Transport<alice::Message, bob::Message>,
bitcoin_wallet: wallet::bitcoin::Wallet,
monero_wallet: wallet::monero::AliceWallet<'a>,
) -> AliceNode<'a> {
Self {
transport,
bitcoin_wallet,
monero_wallet,
}
}
}
pub async fn run_alice_until<'a, R: RngCore + CryptoRng>(
alice: &mut AliceNode<'a>,
initial_state: alice::State,
is_state: fn(&alice::State) -> bool,
rng: &mut R,
) -> Result<alice::State> {
let mut result = initial_state;
loop {
result = alice::next_state(
&alice.bitcoin_wallet,
&alice.monero_wallet,
&mut alice.transport,
result,
rng,
)
.await?;
if is_state(&result) {
return Ok(result);
}
}
}
// TODO: merge this with alice node
// This struct is responsible for I/O
pub struct BobNode<'a> {
transport: Transport<bob::Message, alice::Message>,
pub bitcoin_wallet: wallet::bitcoin::Wallet,
pub monero_wallet: wallet::monero::BobWallet<'a>,
}
impl<'a> BobNode<'a> {
pub fn new(
transport: Transport<bob::Message, alice::Message>,
bitcoin_wallet: wallet::bitcoin::Wallet,
monero_wallet: wallet::monero::BobWallet<'a>,
) -> BobNode<'a> {
Self {
transport,
bitcoin_wallet,
monero_wallet,
}
}
}
pub async fn run_bob_until<'a, R: RngCore + CryptoRng>(
bob: &mut BobNode<'a>,
initial_state: bob::State,
is_state: fn(&bob::State) -> bool,
rng: &mut R,
) -> Result<bob::State> {
let mut result = initial_state;
loop {
result = bob::next_state(
&bob.bitcoin_wallet,
&bob.monero_wallet,
&mut bob.transport,
result,
rng,
)
.await?;
if is_state(&result) {
return Ok(result);
}
}
}