mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-14 10:24:32 -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 std::convert::TryInto;
|
||||||
use tiny_keccak::{Hasher, Keccak};
|
use tiny_keccak::{Hasher, Keccak};
|
||||||
|
|
||||||
|
mod alice;
|
||||||
|
mod bob;
|
||||||
|
|
||||||
const RING_SIZE: usize = 11;
|
const RING_SIZE: usize = 11;
|
||||||
const DOMAIN_TAG: &str = "CSLAG_c";
|
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 {
|
struct DleqProof {
|
||||||
s: Scalar,
|
s: Scalar,
|
||||||
c: Scalar,
|
c: Scalar,
|
||||||
@ -603,6 +325,8 @@ pub struct Message3 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::alice::Alice0;
|
||||||
|
use crate::bob::Bob0;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sign_and_verify_success() {
|
fn sign_and_verify_success() {
|
||||||
|
@ -21,6 +21,7 @@ pub fn init(level: LevelFilter) -> Result<()> {
|
|||||||
builder.init();
|
builder.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing
|
||||||
tracing::info!("Initialized tracing with level: {}", level);
|
tracing::info!("Initialized tracing with level: {}", level);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -27,5 +27,6 @@ pub mod monero;
|
|||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod seed;
|
pub mod seed;
|
||||||
|
mod xmr_first_protocol;
|
||||||
|
|
||||||
mod monero_ext;
|
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