mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-13 09:54:26 -05:00
Add protocol state machine
This commit is contained in:
parent
ccb808bdf7
commit
acb243a0a2
153
monero-adaptor/src/alice.rs
Normal file
153
monero-adaptor/src/alice.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use crate::{
|
||||
final_challenge, AdaptorSignature, Commitment, DleqProof, Message0, Message1, Message2,
|
||||
Message3, Opening, RING_SIZE,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use hash_edwards_to_edwards::hash_point_to_point;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
pub enum AliceState {
|
||||
WaitingForCommitment(Alice0),
|
||||
WaitingForScalar(Alice1),
|
||||
PublishXmr,
|
||||
RedeemBtc,
|
||||
PunishXmr,
|
||||
}
|
||||
|
||||
pub struct Alice0 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
fake_responses: [Scalar; RING_SIZE - 1],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
// this is not s_a cos of something to with one-time-address??
|
||||
s_prime_a: Scalar,
|
||||
// secret value:
|
||||
alpha_a: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_a: EdwardsPoint,
|
||||
I_hat_a: EdwardsPoint,
|
||||
T_a: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl Alice0 {
|
||||
pub fn new(
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
R_a: EdwardsPoint,
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_prime_a: Scalar,
|
||||
) -> Result<Self> {
|
||||
let mut fake_responses = [Scalar::zero(); RING_SIZE - 1];
|
||||
for response in fake_responses.iter_mut().take(RING_SIZE - 1) {
|
||||
*response = Scalar::random(&mut OsRng);
|
||||
}
|
||||
let alpha_a = Scalar::random(&mut OsRng);
|
||||
|
||||
let p_k = ring[0];
|
||||
let H_p_pk = hash_point_to_point(p_k);
|
||||
|
||||
let I_a = s_prime_a * H_p_pk;
|
||||
let I_hat_a = alpha_a * H_p_pk;
|
||||
let T_a = alpha_a * ED25519_BASEPOINT_POINT;
|
||||
|
||||
Ok(Alice0 {
|
||||
ring,
|
||||
fake_responses,
|
||||
msg,
|
||||
R_a,
|
||||
R_prime_a,
|
||||
s_prime_a,
|
||||
alpha_a,
|
||||
H_p_pk,
|
||||
I_a,
|
||||
I_hat_a,
|
||||
T_a,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_message(&self) -> Message0 {
|
||||
Message0 {
|
||||
pi_a: DleqProof::new(
|
||||
ED25519_BASEPOINT_POINT,
|
||||
self.T_a,
|
||||
self.H_p_pk,
|
||||
self.I_hat_a,
|
||||
self.alpha_a,
|
||||
),
|
||||
c_a: Commitment::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message1) -> Result<Alice1> {
|
||||
msg.pi_b
|
||||
.verify(ED25519_BASEPOINT_POINT, msg.T_b, self.H_p_pk, msg.I_hat_b)?;
|
||||
|
||||
let (h_last, h_0) = final_challenge(
|
||||
self.fake_responses,
|
||||
self.ring,
|
||||
self.T_a,
|
||||
msg.T_b,
|
||||
self.R_a,
|
||||
self.I_hat_a,
|
||||
msg.I_hat_b,
|
||||
self.R_prime_a,
|
||||
self.I_a,
|
||||
msg.I_b,
|
||||
self.msg,
|
||||
)?;
|
||||
|
||||
let s_0_a = self.alpha_a - h_last * self.s_prime_a;
|
||||
|
||||
Ok(Alice1 {
|
||||
fake_responses: self.fake_responses,
|
||||
h_0,
|
||||
I_b: msg.I_b,
|
||||
s_0_a,
|
||||
I_a: self.I_a,
|
||||
I_hat_a: self.I_hat_a,
|
||||
T_a: self.T_a,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice1 {
|
||||
fake_responses: [Scalar; RING_SIZE - 1],
|
||||
I_a: EdwardsPoint,
|
||||
I_hat_a: EdwardsPoint,
|
||||
T_a: EdwardsPoint,
|
||||
h_0: Scalar,
|
||||
I_b: EdwardsPoint,
|
||||
s_0_a: Scalar,
|
||||
}
|
||||
|
||||
impl Alice1 {
|
||||
pub fn next_message(&self) -> Message2 {
|
||||
Message2 {
|
||||
d_a: Opening::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
||||
s_0_a: self.s_0_a,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message3) -> Alice2 {
|
||||
let adaptor_sig = AdaptorSignature {
|
||||
s_0_a: self.s_0_a,
|
||||
s_0_b: msg.s_0_b,
|
||||
fake_responses: self.fake_responses,
|
||||
h_0: self.h_0,
|
||||
I: self.I_a + self.I_b,
|
||||
};
|
||||
|
||||
Alice2 { adaptor_sig }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice2 {
|
||||
pub adaptor_sig: AdaptorSignature,
|
||||
}
|
164
monero-adaptor/src/bob.rs
Normal file
164
monero-adaptor/src/bob.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use crate::{
|
||||
final_challenge, AdaptorSignature, Commitment, DleqProof, Message0, Message1, Message2,
|
||||
Message3, RING_SIZE,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use hash_edwards_to_edwards::hash_point_to_point;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
pub enum BobState {
|
||||
WaitingForCommitment(Bob0),
|
||||
WaitingForOpening(Bob1),
|
||||
PublishXmr,
|
||||
RedeemBtc,
|
||||
PunishXmr,
|
||||
}
|
||||
|
||||
pub struct Bob0 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
// secret value:
|
||||
alpha_b: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_b: EdwardsPoint,
|
||||
I_hat_b: EdwardsPoint,
|
||||
T_b: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl Bob0 {
|
||||
pub fn new(
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
R_a: EdwardsPoint,
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
) -> Result<Self> {
|
||||
let alpha_b = Scalar::random(&mut OsRng);
|
||||
|
||||
let p_k = ring[0];
|
||||
let H_p_pk = hash_point_to_point(p_k);
|
||||
|
||||
let I_b = s_b * H_p_pk;
|
||||
let I_hat_b = alpha_b * H_p_pk;
|
||||
let T_b = alpha_b * ED25519_BASEPOINT_POINT;
|
||||
|
||||
Ok(Bob0 {
|
||||
ring,
|
||||
msg,
|
||||
R_a,
|
||||
R_prime_a,
|
||||
s_b,
|
||||
alpha_b,
|
||||
H_p_pk,
|
||||
I_b,
|
||||
I_hat_b,
|
||||
T_b,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message0) -> Bob1 {
|
||||
Bob1 {
|
||||
ring: self.ring,
|
||||
msg: self.msg,
|
||||
R_a: self.R_a,
|
||||
R_prime_a: self.R_prime_a,
|
||||
s_b: self.s_b,
|
||||
alpha_b: self.alpha_b,
|
||||
H_p_pk: self.H_p_pk,
|
||||
I_b: self.I_b,
|
||||
I_hat_b: self.I_hat_b,
|
||||
T_b: self.T_b,
|
||||
pi_a: msg.pi_a,
|
||||
c_a: msg.c_a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bob1 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
// secret value:
|
||||
alpha_b: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_b: EdwardsPoint,
|
||||
I_hat_b: EdwardsPoint,
|
||||
T_b: EdwardsPoint,
|
||||
pi_a: DleqProof,
|
||||
c_a: Commitment,
|
||||
}
|
||||
|
||||
impl Bob1 {
|
||||
pub fn next_message(&self) -> Message1 {
|
||||
Message1 {
|
||||
I_b: self.I_b,
|
||||
T_b: self.T_b,
|
||||
I_hat_b: self.I_hat_b,
|
||||
pi_b: DleqProof::new(
|
||||
ED25519_BASEPOINT_POINT,
|
||||
self.T_b,
|
||||
self.H_p_pk,
|
||||
self.I_hat_b,
|
||||
self.alpha_b,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message2) -> Result<Bob2> {
|
||||
let (fake_responses, I_a, I_hat_a, T_a) = msg.d_a.open(self.c_a)?;
|
||||
|
||||
self.pi_a
|
||||
.verify(ED25519_BASEPOINT_POINT, T_a, self.H_p_pk, I_hat_a)?;
|
||||
|
||||
let (h_last, h_0) = final_challenge(
|
||||
fake_responses,
|
||||
self.ring,
|
||||
T_a,
|
||||
self.T_b,
|
||||
self.R_a,
|
||||
I_hat_a,
|
||||
self.I_hat_b,
|
||||
self.R_prime_a,
|
||||
I_a,
|
||||
self.I_b,
|
||||
self.msg,
|
||||
)?;
|
||||
|
||||
let s_0_b = self.alpha_b - h_last * self.s_b;
|
||||
|
||||
let adaptor_sig = AdaptorSignature {
|
||||
s_0_a: msg.s_0_a,
|
||||
s_0_b,
|
||||
fake_responses,
|
||||
h_0,
|
||||
I: I_a + self.I_b,
|
||||
};
|
||||
|
||||
Ok(Bob2 { s_0_b, adaptor_sig })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bob2 {
|
||||
s_0_b: Scalar,
|
||||
pub adaptor_sig: AdaptorSignature,
|
||||
}
|
||||
|
||||
impl Bob2 {
|
||||
pub fn next_message(&self) -> Message3 {
|
||||
Message3 { s_0_b: self.s_0_b }
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@ use rand::rngs::OsRng;
|
||||
use std::convert::TryInto;
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
mod alice;
|
||||
mod bob;
|
||||
|
||||
const RING_SIZE: usize = 11;
|
||||
const DOMAIN_TAG: &str = "CSLAG_c";
|
||||
|
||||
@ -154,287 +157,6 @@ impl Signature {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice0 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
fake_responses: [Scalar; RING_SIZE - 1],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
// this is not s_a cos of something to with one-time-address??
|
||||
s_prime_a: Scalar,
|
||||
// secret value:
|
||||
alpha_a: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_a: EdwardsPoint,
|
||||
I_hat_a: EdwardsPoint,
|
||||
T_a: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl Alice0 {
|
||||
pub fn new(
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
R_a: EdwardsPoint,
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_prime_a: Scalar,
|
||||
) -> Result<Self> {
|
||||
let mut fake_responses = [Scalar::zero(); RING_SIZE - 1];
|
||||
for response in fake_responses.iter_mut().take(RING_SIZE - 1) {
|
||||
*response = Scalar::random(&mut OsRng);
|
||||
}
|
||||
let alpha_a = Scalar::random(&mut OsRng);
|
||||
|
||||
let p_k = ring[0];
|
||||
let H_p_pk = hash_point_to_point(p_k);
|
||||
|
||||
let I_a = s_prime_a * H_p_pk;
|
||||
let I_hat_a = alpha_a * H_p_pk;
|
||||
let T_a = alpha_a * ED25519_BASEPOINT_POINT;
|
||||
|
||||
Ok(Alice0 {
|
||||
ring,
|
||||
fake_responses,
|
||||
msg,
|
||||
R_a,
|
||||
R_prime_a,
|
||||
s_prime_a,
|
||||
alpha_a,
|
||||
H_p_pk,
|
||||
I_a,
|
||||
I_hat_a,
|
||||
T_a,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_message(&self) -> Message0 {
|
||||
Message0 {
|
||||
pi_a: DleqProof::new(
|
||||
ED25519_BASEPOINT_POINT,
|
||||
self.T_a,
|
||||
self.H_p_pk,
|
||||
self.I_hat_a,
|
||||
self.alpha_a,
|
||||
),
|
||||
c_a: Commitment::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message1) -> Result<Alice1> {
|
||||
msg.pi_b
|
||||
.verify(ED25519_BASEPOINT_POINT, msg.T_b, self.H_p_pk, msg.I_hat_b)?;
|
||||
|
||||
let (h_last, h_0) = final_challenge(
|
||||
self.fake_responses,
|
||||
self.ring,
|
||||
self.T_a,
|
||||
msg.T_b,
|
||||
self.R_a,
|
||||
self.I_hat_a,
|
||||
msg.I_hat_b,
|
||||
self.R_prime_a,
|
||||
self.I_a,
|
||||
msg.I_b,
|
||||
self.msg,
|
||||
)?;
|
||||
|
||||
let s_0_a = self.alpha_a - h_last * self.s_prime_a;
|
||||
|
||||
Ok(Alice1 {
|
||||
fake_responses: self.fake_responses,
|
||||
h_0,
|
||||
I_b: msg.I_b,
|
||||
s_0_a,
|
||||
I_a: self.I_a,
|
||||
I_hat_a: self.I_hat_a,
|
||||
T_a: self.T_a,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice1 {
|
||||
fake_responses: [Scalar; RING_SIZE - 1],
|
||||
I_a: EdwardsPoint,
|
||||
I_hat_a: EdwardsPoint,
|
||||
T_a: EdwardsPoint,
|
||||
h_0: Scalar,
|
||||
I_b: EdwardsPoint,
|
||||
s_0_a: Scalar,
|
||||
}
|
||||
|
||||
impl Alice1 {
|
||||
pub fn next_message(&self) -> Message2 {
|
||||
Message2 {
|
||||
d_a: Opening::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
||||
s_0_a: self.s_0_a,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message3) -> Alice2 {
|
||||
let adaptor_sig = AdaptorSignature {
|
||||
s_0_a: self.s_0_a,
|
||||
s_0_b: msg.s_0_b,
|
||||
fake_responses: self.fake_responses,
|
||||
h_0: self.h_0,
|
||||
I: self.I_a + self.I_b,
|
||||
};
|
||||
|
||||
Alice2 { adaptor_sig }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice2 {
|
||||
pub adaptor_sig: AdaptorSignature,
|
||||
}
|
||||
|
||||
pub struct Bob0 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
// secret value:
|
||||
alpha_b: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_b: EdwardsPoint,
|
||||
I_hat_b: EdwardsPoint,
|
||||
T_b: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl Bob0 {
|
||||
pub fn new(
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
R_a: EdwardsPoint,
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
) -> Result<Self> {
|
||||
let alpha_b = Scalar::random(&mut OsRng);
|
||||
|
||||
let p_k = ring[0];
|
||||
let H_p_pk = hash_point_to_point(p_k);
|
||||
|
||||
let I_b = s_b * H_p_pk;
|
||||
let I_hat_b = alpha_b * H_p_pk;
|
||||
let T_b = alpha_b * ED25519_BASEPOINT_POINT;
|
||||
|
||||
Ok(Bob0 {
|
||||
ring,
|
||||
msg,
|
||||
R_a,
|
||||
R_prime_a,
|
||||
s_b,
|
||||
alpha_b,
|
||||
H_p_pk,
|
||||
I_b,
|
||||
I_hat_b,
|
||||
T_b,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message0) -> Bob1 {
|
||||
Bob1 {
|
||||
ring: self.ring,
|
||||
msg: self.msg,
|
||||
R_a: self.R_a,
|
||||
R_prime_a: self.R_prime_a,
|
||||
s_b: self.s_b,
|
||||
alpha_b: self.alpha_b,
|
||||
H_p_pk: self.H_p_pk,
|
||||
I_b: self.I_b,
|
||||
I_hat_b: self.I_hat_b,
|
||||
T_b: self.T_b,
|
||||
pi_a: msg.pi_a,
|
||||
c_a: msg.c_a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bob1 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
msg: [u8; 32],
|
||||
// encryption key
|
||||
R_a: EdwardsPoint,
|
||||
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
||||
R_prime_a: EdwardsPoint,
|
||||
s_b: Scalar,
|
||||
// secret value:
|
||||
alpha_b: Scalar,
|
||||
H_p_pk: EdwardsPoint,
|
||||
I_b: EdwardsPoint,
|
||||
I_hat_b: EdwardsPoint,
|
||||
T_b: EdwardsPoint,
|
||||
pi_a: DleqProof,
|
||||
c_a: Commitment,
|
||||
}
|
||||
|
||||
impl Bob1 {
|
||||
pub fn next_message(&self) -> Message1 {
|
||||
Message1 {
|
||||
I_b: self.I_b,
|
||||
T_b: self.T_b,
|
||||
I_hat_b: self.I_hat_b,
|
||||
pi_b: DleqProof::new(
|
||||
ED25519_BASEPOINT_POINT,
|
||||
self.T_b,
|
||||
self.H_p_pk,
|
||||
self.I_hat_b,
|
||||
self.alpha_b,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message2) -> Result<Bob2> {
|
||||
let (fake_responses, I_a, I_hat_a, T_a) = msg.d_a.open(self.c_a)?;
|
||||
|
||||
self.pi_a
|
||||
.verify(ED25519_BASEPOINT_POINT, T_a, self.H_p_pk, I_hat_a)?;
|
||||
|
||||
let (h_last, h_0) = final_challenge(
|
||||
fake_responses,
|
||||
self.ring,
|
||||
T_a,
|
||||
self.T_b,
|
||||
self.R_a,
|
||||
I_hat_a,
|
||||
self.I_hat_b,
|
||||
self.R_prime_a,
|
||||
I_a,
|
||||
self.I_b,
|
||||
self.msg,
|
||||
)?;
|
||||
|
||||
let s_0_b = self.alpha_b - h_last * self.s_b;
|
||||
|
||||
let adaptor_sig = AdaptorSignature {
|
||||
s_0_a: msg.s_0_a,
|
||||
s_0_b,
|
||||
fake_responses,
|
||||
h_0,
|
||||
I: I_a + self.I_b,
|
||||
};
|
||||
|
||||
Ok(Bob2 { s_0_b, adaptor_sig })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bob2 {
|
||||
s_0_b: Scalar,
|
||||
pub adaptor_sig: AdaptorSignature,
|
||||
}
|
||||
|
||||
impl Bob2 {
|
||||
pub fn next_message(&self) -> Message3 {
|
||||
Message3 { s_0_b: self.s_0_b }
|
||||
}
|
||||
}
|
||||
|
||||
struct DleqProof {
|
||||
s: Scalar,
|
||||
c: Scalar,
|
||||
@ -603,6 +325,8 @@ pub struct Message3 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::alice::Alice0;
|
||||
use crate::bob::Bob0;
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_success() {
|
||||
|
@ -21,6 +21,7 @@ pub fn init(level: LevelFilter) -> Result<()> {
|
||||
builder.init();
|
||||
}
|
||||
|
||||
tracing
|
||||
tracing::info!("Initialized tracing with level: {}", level);
|
||||
|
||||
Ok(())
|
||||
|
@ -27,5 +27,6 @@ pub mod monero;
|
||||
pub mod network;
|
||||
pub mod protocol;
|
||||
pub mod seed;
|
||||
mod xmr_first_protocol;
|
||||
|
||||
mod monero_ext;
|
||||
|
6
swap/src/xmr_first_protocol.rs
Normal file
6
swap/src/xmr_first_protocol.rs
Normal file
@ -0,0 +1,6 @@
|
||||
mod alice;
|
||||
mod bob;
|
||||
|
||||
pub trait Persist {
|
||||
fn persist(&self);
|
||||
}
|
133
swap/src/xmr_first_protocol/alice.rs
Normal file
133
swap/src/xmr_first_protocol/alice.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use crate::xmr_first_protocol::Persist;
|
||||
use std::collections::VecDeque;
|
||||
use std::task::Poll;
|
||||
|
||||
pub struct StateMachine {
|
||||
state: State,
|
||||
actions: VecDeque<Action>,
|
||||
events: VecDeque<Event>,
|
||||
}
|
||||
|
||||
impl StateMachine {
|
||||
fn inject_event(&mut self, event: Event) {
|
||||
match self.state {
|
||||
State::WatchingForBtcLock => match event {
|
||||
Event::BtcLockSeenInMempool => {
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcRedeem);
|
||||
self.actions.push_back(Action::WatchForXmrRedeem);
|
||||
self.state = State::WatchingForXmrRedeem;
|
||||
}
|
||||
Event::BtcLockTimeoutElapsed => {
|
||||
self.actions.push_back(Action::BroadcastXmrRefund);
|
||||
self.state = State::Aborted;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
State::WatchingForXmrRedeem => match event {
|
||||
Event::T2Elapsed => {
|
||||
self.actions.push_back(Action::BroadcastXmrRefund);
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcPunish);
|
||||
self.state = State::Punished;
|
||||
}
|
||||
Event::XmrRedeemSeenInMempool => {
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcPunish);
|
||||
self.state = State::Success;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
// State::WaitingForT2 => match event {
|
||||
// Event::T2Elapsed => {
|
||||
// self.actions.push_back(Action::SignAndBroadcastBtcPunish);
|
||||
// self.actions.push_back(Action::BroadcastXmrRefund);
|
||||
// self.state = State::Success;
|
||||
// }
|
||||
// _ => {}
|
||||
// },
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> Poll<Action> {
|
||||
if let Some(action) = self.actions.pop_front() {
|
||||
Poll::Ready(action)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Persist for Event {
|
||||
fn persist(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum State {
|
||||
WatchingForBtcLock,
|
||||
WatchingForXmrRedeem,
|
||||
// WaitingForT2,
|
||||
Punished,
|
||||
Success,
|
||||
Aborted,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
BtcLockSeenInMempool,
|
||||
T2Elapsed,
|
||||
BtcLockTimeoutElapsed,
|
||||
XmrRedeemSeenInMempool,
|
||||
}
|
||||
|
||||
// These actions should not fail (are retried until successful) and should be
|
||||
// idempotent This allows us to greatly simplify the state machine
|
||||
pub enum Action {
|
||||
WatchForXmrRedeem,
|
||||
SignAndBroadcastBtcPunish,
|
||||
SignAndBroadcastBtcRedeem,
|
||||
BroadcastXmrRefund,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn happy_path() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForBtcLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.inject_event(Event::BtcLockSeenInMempool);
|
||||
assert_eq!(state_machine.poll())
|
||||
state_machine.inject_event(Event::XmrRedeemSeenInMempool);
|
||||
assert_eq!(state_machine.state, State::Success);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn bob_fails_to_lock_btc() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForBtcLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.events.push_back(Event::BtcLockTimeoutElapsed);
|
||||
state_machine.run();
|
||||
assert_eq!(state_machine.state, State::Aborted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bob_fails_to_redeem_xmr_before_t2() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForBtcLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.events.push_back(Event::BtcLockSeenInMempool);
|
||||
state_machine.events.push_back(Event::T2Elapsed);
|
||||
state_machine.run();
|
||||
assert_eq!(state_machine.state, State::Punished);
|
||||
}
|
||||
}
|
127
swap/src/xmr_first_protocol/bob.rs
Normal file
127
swap/src/xmr_first_protocol/bob.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::monero::TransferProof;
|
||||
use crate::xmr_first_protocol::Persist;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct StateMachine {
|
||||
state: State,
|
||||
actions: VecDeque<Action>,
|
||||
events: VecDeque<Event>,
|
||||
}
|
||||
|
||||
impl StateMachine {
|
||||
fn next(&mut self, event: Event) {
|
||||
match self.state {
|
||||
State::WatchingForXmrLock => match event {
|
||||
Event::XmrConfirmed => {
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcLock);
|
||||
self.state = State::WaitingForBtcRedeem;
|
||||
}
|
||||
Event::T1Elapsed => {
|
||||
self.state = State::Aborted;
|
||||
}
|
||||
Event::XmrRefundSeenInMempool => {
|
||||
self.state = State::Aborted;
|
||||
}
|
||||
_ => panic!("unhandled scenario"),
|
||||
},
|
||||
State::WaitingForBtcRedeem => match event {
|
||||
Event::BtcRedeemSeenInMempool => {
|
||||
self.actions.push_back(Action::BroadcastXmrRedeem);
|
||||
self.state = State::Success;
|
||||
}
|
||||
Event::T1Elapsed => {
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcRefund);
|
||||
self.state = State::Refunded;
|
||||
}
|
||||
Event::XmrRefundSeenInMempool => {
|
||||
self.actions.push_back(Action::SignAndBroadcastBtcRefund);
|
||||
self.state = State::Refunded;
|
||||
}
|
||||
_ => panic!("unhandled scenario"),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
while let Some(event) = self.events.pop_front() {
|
||||
self.next(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Persist for Event {
|
||||
fn persist(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum State {
|
||||
WatchingForXmrLock,
|
||||
WaitingForBtcRedeem,
|
||||
Success,
|
||||
Refunded,
|
||||
Aborted,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
// todo: do we simply wait for confirmations are is mempool sufficient?
|
||||
XmrConfirmed,
|
||||
// This will contain the s_a allowing bob to build xmr_redeem
|
||||
BtcRedeemSeenInMempool,
|
||||
XmrRefundSeenInMempool,
|
||||
T1Elapsed,
|
||||
}
|
||||
|
||||
pub enum Action {
|
||||
SignAndBroadcastBtcLock,
|
||||
BroadcastXmrRedeem,
|
||||
SignAndBroadcastBtcRefund,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn happy_path() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForXmrLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.events.push_back(Event::XmrConfirmed);
|
||||
state_machine
|
||||
.events
|
||||
.push_back(Event::BtcRedeemSeenInMempool);
|
||||
state_machine.run();
|
||||
assert_eq!(state_machine.state, State::Success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alice_fails_to_redeem_btc_before_t1() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForXmrLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.events.push_back(Event::XmrConfirmed);
|
||||
state_machine.events.push_back(Event::T1Elapsed);
|
||||
state_machine.run();
|
||||
assert_eq!(state_machine.state, State::Refunded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alice_tries_to_refund_xmr_after_redeeming_btc() {
|
||||
let mut state_machine = StateMachine {
|
||||
state: State::WatchingForXmrLock,
|
||||
actions: Default::default(),
|
||||
events: Default::default(),
|
||||
};
|
||||
state_machine.events.push_back(Event::XmrConfirmed);
|
||||
state_machine.events.push_back(Event::T1Elapsed);
|
||||
state_machine.run();
|
||||
assert_eq!(state_machine.state, State::Refunded);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user