2020-09-28 02:18:50 -04:00
|
|
|
use crate::{
|
|
|
|
alice,
|
2020-09-29 01:36:50 -04:00
|
|
|
bitcoin::{
|
2020-10-23 08:05:34 -04:00
|
|
|
self, poll_until_block_height_is_gte, BroadcastSignedTransaction, BuildTxLockPsbt,
|
|
|
|
SignTxLock, TxCancel, WatchForRawTransaction,
|
2020-09-29 01:36:50 -04:00
|
|
|
},
|
2020-09-28 02:18:50 -04:00
|
|
|
monero,
|
2020-10-22 00:06:05 -04:00
|
|
|
serde::monero_private_key,
|
2020-10-08 18:46:24 -04:00
|
|
|
transport::{ReceiveMessage, SendMessage},
|
2020-09-28 02:18:50 -04:00
|
|
|
};
|
|
|
|
use anyhow::{anyhow, Result};
|
2020-10-23 08:05:34 -04:00
|
|
|
use async_trait::async_trait;
|
2020-09-28 02:18:50 -04:00
|
|
|
use ecdsa_fun::{
|
|
|
|
adaptor::{Adaptor, EncryptedSignature},
|
|
|
|
nonce::Deterministic,
|
|
|
|
Signature,
|
|
|
|
};
|
2020-10-23 08:05:34 -04:00
|
|
|
use futures::{
|
|
|
|
future::{select, Either},
|
2020-10-25 20:36:52 -04:00
|
|
|
pin_mut, FutureExt,
|
2020-10-23 08:05:34 -04:00
|
|
|
};
|
|
|
|
use genawaiter::sync::{Gen, GenBoxed};
|
2020-09-28 02:18:50 -04:00
|
|
|
use rand::{CryptoRng, RngCore};
|
2020-10-13 18:32:25 -04:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-09-28 02:18:50 -04:00
|
|
|
use sha2::Sha256;
|
2020-10-23 08:05:34 -04:00
|
|
|
use std::{
|
|
|
|
convert::{TryFrom, TryInto},
|
|
|
|
sync::Arc,
|
|
|
|
time::Duration,
|
|
|
|
};
|
2020-10-26 22:26:40 -04:00
|
|
|
use tokio::{sync::Mutex, time::timeout};
|
2020-10-23 08:05:34 -04:00
|
|
|
use tracing::error;
|
2020-09-29 01:36:50 -04:00
|
|
|
|
|
|
|
pub mod message;
|
2020-11-11 19:06:34 -05:00
|
|
|
use crate::{
|
|
|
|
bitcoin::{BlockHeight, GetRawTransaction, TransactionBlockHeight},
|
|
|
|
monero::{CreateWalletForOutput, WatchForTransfer},
|
|
|
|
};
|
|
|
|
use ::bitcoin::{Transaction, Txid};
|
2020-10-08 19:17:36 -04:00
|
|
|
pub use message::{Message, Message0, Message1, Message2, Message3};
|
2020-09-29 01:36:50 -04:00
|
|
|
|
2020-10-23 08:05:34 -04:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Action {
|
|
|
|
LockBtc(bitcoin::TxLock),
|
|
|
|
SendBtcRedeemEncsig(bitcoin::EncryptedSignature),
|
|
|
|
CreateXmrWalletForOutput {
|
|
|
|
spend_key: monero::PrivateKey,
|
|
|
|
view_key: monero::PrivateViewKey,
|
|
|
|
},
|
|
|
|
CancelBtc(bitcoin::Transaction),
|
|
|
|
RefundBtc(bitcoin::Transaction),
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: This could be moved to the monero module
|
|
|
|
#[async_trait]
|
|
|
|
pub trait ReceiveTransferProof {
|
|
|
|
async fn receive_transfer_proof(&mut self) -> monero::TransferProof;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform the on-chain protocol to swap monero and bitcoin as Bob.
|
|
|
|
///
|
|
|
|
/// This is called post handshake, after all the keys, addresses and most of the
|
|
|
|
/// signatures have been exchanged.
|
2020-10-25 21:03:14 -04:00
|
|
|
///
|
|
|
|
/// The argument `bitcoin_tx_lock_timeout` is used to determine how long we will
|
|
|
|
/// wait for Bob, the caller of this function, to lock up the bitcoin.
|
2020-10-23 08:05:34 -04:00
|
|
|
pub fn action_generator<N, M, B>(
|
2020-10-26 22:26:40 -04:00
|
|
|
network: Arc<Mutex<N>>,
|
2020-10-23 08:05:34 -04:00
|
|
|
monero_client: Arc<M>,
|
|
|
|
bitcoin_client: Arc<B>,
|
|
|
|
// TODO: Replace this with a new, slimmer struct?
|
|
|
|
State2 {
|
|
|
|
A,
|
|
|
|
b,
|
|
|
|
s_b,
|
|
|
|
S_a_monero,
|
|
|
|
S_a_bitcoin,
|
|
|
|
v,
|
|
|
|
xmr,
|
|
|
|
refund_timelock,
|
|
|
|
redeem_address,
|
|
|
|
refund_address,
|
|
|
|
tx_lock,
|
|
|
|
tx_cancel_sig_a,
|
|
|
|
tx_refund_encsig,
|
|
|
|
..
|
|
|
|
}: State2,
|
2020-10-25 21:03:14 -04:00
|
|
|
bitcoin_tx_lock_timeout: u64,
|
2020-10-23 08:05:34 -04:00
|
|
|
) -> GenBoxed<Action, (), ()>
|
|
|
|
where
|
2020-10-26 22:26:40 -04:00
|
|
|
N: ReceiveTransferProof + Send + 'static,
|
2020-10-23 08:05:34 -04:00
|
|
|
M: monero::WatchForTransfer + Send + Sync + 'static,
|
|
|
|
B: bitcoin::BlockHeight
|
|
|
|
+ bitcoin::TransactionBlockHeight
|
|
|
|
+ bitcoin::WatchForRawTransaction
|
|
|
|
+ Send
|
|
|
|
+ Sync
|
|
|
|
+ 'static,
|
|
|
|
{
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum SwapFailed {
|
2020-10-25 20:33:51 -04:00
|
|
|
BeforeBtcLock(Reason),
|
2020-10-23 08:05:34 -04:00
|
|
|
AfterBtcLock(Reason),
|
|
|
|
AfterBtcRedeem(Reason),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reason why the swap has failed.
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Reason {
|
2020-10-25 20:33:51 -04:00
|
|
|
/// Bob was too slow to lock the bitcoin.
|
|
|
|
InactiveBob,
|
2020-10-23 08:05:34 -04:00
|
|
|
/// The refund timelock has been reached.
|
|
|
|
BtcExpired,
|
|
|
|
/// Alice did not lock up enough monero in the shared output.
|
|
|
|
InsufficientXmr(monero::InsufficientFunds),
|
|
|
|
/// Could not find Bob's signature on the redeem transaction witness
|
|
|
|
/// stack.
|
|
|
|
BtcRedeemSignature,
|
|
|
|
/// Could not recover secret `s_a` from Bob's redeem transaction
|
|
|
|
/// signature.
|
|
|
|
SecretRecovery,
|
|
|
|
}
|
|
|
|
|
|
|
|
Gen::new_boxed(|co| async move {
|
|
|
|
let swap_result: Result<(), SwapFailed> = async {
|
|
|
|
co.yield_(Action::LockBtc(tx_lock.clone())).await;
|
|
|
|
|
|
|
|
timeout(
|
2020-10-25 21:03:14 -04:00
|
|
|
Duration::from_secs(bitcoin_tx_lock_timeout),
|
2020-10-23 08:05:34 -04:00
|
|
|
bitcoin_client.watch_for_raw_transaction(tx_lock.txid()),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.map(|tx| tx.txid())
|
2020-10-25 20:33:51 -04:00
|
|
|
.map_err(|_| SwapFailed::BeforeBtcLock(Reason::InactiveBob))?;
|
2020-10-23 08:05:34 -04:00
|
|
|
|
|
|
|
let tx_lock_height = bitcoin_client
|
|
|
|
.transaction_block_height(tx_lock.txid())
|
|
|
|
.await;
|
|
|
|
let poll_until_btc_has_expired = poll_until_block_height_is_gte(
|
|
|
|
bitcoin_client.as_ref(),
|
|
|
|
tx_lock_height + refund_timelock,
|
|
|
|
)
|
|
|
|
.shared();
|
2020-10-25 20:36:52 -04:00
|
|
|
pin_mut!(poll_until_btc_has_expired);
|
2020-10-23 08:05:34 -04:00
|
|
|
|
2020-10-26 22:26:40 -04:00
|
|
|
let transfer_proof = {
|
|
|
|
let mut guard = network.as_ref().lock().await;
|
|
|
|
let transfer_proof = match select(
|
|
|
|
guard.receive_transfer_proof(),
|
|
|
|
poll_until_btc_has_expired.clone(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Either::Left((proof, _)) => proof,
|
|
|
|
Either::Right(_) => return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired)),
|
|
|
|
};
|
|
|
|
|
2020-10-29 18:26:52 -04:00
|
|
|
tracing::debug!("select returned transfer proof from message");
|
|
|
|
|
2020-10-26 22:26:40 -04:00
|
|
|
transfer_proof
|
2020-10-23 08:05:34 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
let S_b_monero = monero::PublicKey::from_private_key(&monero::PrivateKey::from_scalar(
|
|
|
|
s_b.into_ed25519(),
|
|
|
|
));
|
|
|
|
let S = S_a_monero + S_b_monero;
|
|
|
|
|
|
|
|
match select(
|
|
|
|
monero_client.watch_for_transfer(S, v.public(), transfer_proof, xmr, 0),
|
|
|
|
poll_until_btc_has_expired.clone(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Either::Left((Err(e), _)) => {
|
|
|
|
return Err(SwapFailed::AfterBtcLock(Reason::InsufficientXmr(e)))
|
|
|
|
}
|
|
|
|
Either::Right(_) => return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired)),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&tx_lock, &redeem_address);
|
|
|
|
let tx_redeem_encsig = b.encsign(S_a_bitcoin.clone(), tx_redeem.digest());
|
|
|
|
|
|
|
|
co.yield_(Action::SendBtcRedeemEncsig(tx_redeem_encsig.clone()))
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let tx_redeem_published = match select(
|
|
|
|
bitcoin_client.watch_for_raw_transaction(tx_redeem.txid()),
|
|
|
|
poll_until_btc_has_expired,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Either::Left((tx, _)) => tx,
|
|
|
|
Either::Right(_) => return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired)),
|
|
|
|
};
|
|
|
|
|
|
|
|
let tx_redeem_sig = tx_redeem
|
|
|
|
.extract_signature_by_key(tx_redeem_published, b.public())
|
|
|
|
.map_err(|_| SwapFailed::AfterBtcRedeem(Reason::BtcRedeemSignature))?;
|
|
|
|
let s_a = bitcoin::recover(S_a_bitcoin, tx_redeem_sig, tx_redeem_encsig)
|
|
|
|
.map_err(|_| SwapFailed::AfterBtcRedeem(Reason::SecretRecovery))?;
|
2020-11-08 19:07:06 -05:00
|
|
|
let s_a = monero::private_key_from_secp256k1_scalar(s_a.into());
|
2020-10-23 08:05:34 -04:00
|
|
|
|
|
|
|
let s_b = monero::PrivateKey {
|
|
|
|
scalar: s_b.into_ed25519(),
|
|
|
|
};
|
|
|
|
|
|
|
|
co.yield_(Action::CreateXmrWalletForOutput {
|
|
|
|
spend_key: s_a + s_b,
|
|
|
|
view_key: v,
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.await;
|
|
|
|
|
|
|
|
if let Err(ref err) = swap_result {
|
|
|
|
error!("swap failed: {:?}", err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Err(SwapFailed::AfterBtcLock(_)) = swap_result {
|
|
|
|
let tx_cancel =
|
|
|
|
bitcoin::TxCancel::new(&tx_lock, refund_timelock, A.clone(), b.public());
|
|
|
|
let tx_cancel_txid = tx_cancel.txid();
|
|
|
|
let signed_tx_cancel = {
|
|
|
|
let sig_a = tx_cancel_sig_a.clone();
|
|
|
|
let sig_b = b.sign(tx_cancel.digest());
|
|
|
|
|
|
|
|
tx_cancel
|
|
|
|
.clone()
|
|
|
|
.add_signatures(&tx_lock, (A.clone(), sig_a), (b.public(), sig_b))
|
|
|
|
.expect("sig_{a,b} to be valid signatures for tx_cancel")
|
|
|
|
};
|
|
|
|
|
|
|
|
co.yield_(Action::CancelBtc(signed_tx_cancel)).await;
|
|
|
|
|
|
|
|
let _ = bitcoin_client
|
|
|
|
.watch_for_raw_transaction(tx_cancel_txid)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &refund_address);
|
|
|
|
let tx_refund_txid = tx_refund.txid();
|
|
|
|
let signed_tx_refund = {
|
|
|
|
let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default();
|
|
|
|
|
|
|
|
let sig_a =
|
|
|
|
adaptor.decrypt_signature(&s_b.into_secp256k1(), tx_refund_encsig.clone());
|
|
|
|
let sig_b = b.sign(tx_refund.digest());
|
|
|
|
|
|
|
|
tx_refund
|
|
|
|
.add_signatures(&tx_cancel, (A.clone(), sig_a), (b.public(), sig_b))
|
|
|
|
.expect("sig_{a,b} to be valid signatures for tx_refund")
|
|
|
|
};
|
|
|
|
|
|
|
|
co.yield_(Action::RefundBtc(signed_tx_refund)).await;
|
|
|
|
|
|
|
|
let _ = bitcoin_client
|
|
|
|
.watch_for_raw_transaction(tx_refund_txid)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-10-11 17:53:21 -04:00
|
|
|
// There are no guarantees that send_message and receive_massage do not block
|
|
|
|
// the flow of execution. Therefore they must be paired between Alice/Bob, one
|
|
|
|
// send to one receive in the correct order.
|
2020-09-29 01:36:50 -04:00
|
|
|
pub async fn next_state<
|
|
|
|
R: RngCore + CryptoRng,
|
2020-10-07 19:53:30 -04:00
|
|
|
B: WatchForRawTransaction + SignTxLock + BuildTxLockPsbt + BroadcastSignedTransaction,
|
2020-10-14 22:10:31 -04:00
|
|
|
M: CreateWalletForOutput + WatchForTransfer,
|
2020-10-08 18:46:24 -04:00
|
|
|
T: SendMessage<Message> + ReceiveMessage<alice::Message>,
|
2020-09-29 01:36:50 -04:00
|
|
|
>(
|
|
|
|
bitcoin_wallet: &B,
|
|
|
|
monero_wallet: &M,
|
|
|
|
transport: &mut T,
|
|
|
|
state: State,
|
|
|
|
rng: &mut R,
|
|
|
|
) -> Result<State> {
|
|
|
|
match state {
|
|
|
|
State::State0(state0) => {
|
|
|
|
transport
|
|
|
|
.send_message(state0.next_message(rng).into())
|
|
|
|
.await?;
|
2020-10-08 17:48:00 -04:00
|
|
|
let message0 = transport.receive_message().await?.try_into()?;
|
2020-09-29 01:36:50 -04:00
|
|
|
let state1 = state0.receive(bitcoin_wallet, message0).await?;
|
|
|
|
Ok(state1.into())
|
|
|
|
}
|
|
|
|
State::State1(state1) => {
|
|
|
|
transport.send_message(state1.next_message().into()).await?;
|
2020-09-28 02:18:50 -04:00
|
|
|
|
2020-10-08 17:48:00 -04:00
|
|
|
let message1 = transport.receive_message().await?.try_into()?;
|
2020-09-29 01:36:50 -04:00
|
|
|
let state2 = state1.receive(message1)?;
|
2020-10-21 19:57:42 -04:00
|
|
|
|
|
|
|
let message2 = state2.next_message();
|
|
|
|
transport.send_message(message2.into()).await?;
|
2020-09-29 01:36:50 -04:00
|
|
|
Ok(state2.into())
|
|
|
|
}
|
|
|
|
State::State2(state2) => {
|
|
|
|
let state3 = state2.lock_btc(bitcoin_wallet).await?;
|
|
|
|
tracing::info!("bob has locked btc");
|
2020-10-21 19:57:42 -04:00
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
Ok(state3.into())
|
|
|
|
}
|
|
|
|
State::State3(state3) => {
|
2020-10-08 17:48:00 -04:00
|
|
|
let message2 = transport.receive_message().await?.try_into()?;
|
2020-09-29 01:36:50 -04:00
|
|
|
let state4 = state3.watch_for_lock_xmr(monero_wallet, message2).await?;
|
|
|
|
tracing::info!("bob has seen that alice has locked xmr");
|
|
|
|
Ok(state4.into())
|
|
|
|
}
|
|
|
|
State::State4(state4) => {
|
|
|
|
transport.send_message(state4.next_message().into()).await?;
|
|
|
|
tracing::info!("bob is watching for redeem_btc");
|
|
|
|
let state5 = state4.watch_for_redeem_btc(bitcoin_wallet).await?;
|
|
|
|
tracing::info!("bob has seen that alice has redeemed btc");
|
|
|
|
state5.claim_xmr(monero_wallet).await?;
|
|
|
|
tracing::info!("bob has claimed xmr");
|
|
|
|
Ok(state5.into())
|
|
|
|
}
|
|
|
|
State::State5(state5) => Ok(state5.into()),
|
|
|
|
}
|
2020-09-28 02:18:50 -04:00
|
|
|
}
|
|
|
|
|
2020-10-21 22:34:01 -04:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
2020-09-29 01:36:50 -04:00
|
|
|
pub enum State {
|
|
|
|
State0(State0),
|
|
|
|
State1(State1),
|
|
|
|
State2(State2),
|
|
|
|
State3(State3),
|
|
|
|
State4(State4),
|
|
|
|
State5(State5),
|
2020-09-28 02:18:50 -04:00
|
|
|
}
|
|
|
|
|
2020-10-08 19:17:36 -04:00
|
|
|
impl_try_from_parent_enum!(State0, State);
|
|
|
|
impl_try_from_parent_enum!(State1, State);
|
|
|
|
impl_try_from_parent_enum!(State2, State);
|
|
|
|
impl_try_from_parent_enum!(State3, State);
|
|
|
|
impl_try_from_parent_enum!(State4, State);
|
|
|
|
impl_try_from_parent_enum!(State5, State);
|
2020-09-29 01:36:50 -04:00
|
|
|
|
2020-10-08 19:17:36 -04:00
|
|
|
impl_from_child_enum!(State0, State);
|
|
|
|
impl_from_child_enum!(State1, State);
|
|
|
|
impl_from_child_enum!(State2, State);
|
|
|
|
impl_from_child_enum!(State3, State);
|
|
|
|
impl_from_child_enum!(State4, State);
|
|
|
|
impl_from_child_enum!(State5, State);
|
2020-09-29 01:36:50 -04:00
|
|
|
|
2020-10-13 18:32:25 -04:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
2020-09-28 02:18:50 -04:00
|
|
|
pub struct State0 {
|
|
|
|
b: bitcoin::SecretKey,
|
|
|
|
s_b: cross_curve_dleq::Scalar,
|
|
|
|
v_b: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
|
|
|
refund_timelock: u32,
|
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl State0 {
|
|
|
|
pub fn new<R: RngCore + CryptoRng>(
|
|
|
|
rng: &mut R,
|
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
|
|
|
refund_timelock: u32,
|
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
|
|
|
) -> Self {
|
|
|
|
let b = bitcoin::SecretKey::new_random(rng);
|
|
|
|
|
|
|
|
let s_b = cross_curve_dleq::Scalar::random(rng);
|
|
|
|
let v_b = monero::PrivateViewKey::new_random(rng);
|
|
|
|
|
|
|
|
Self {
|
|
|
|
b,
|
|
|
|
s_b,
|
|
|
|
v_b,
|
|
|
|
btc,
|
|
|
|
xmr,
|
|
|
|
refund_timelock,
|
|
|
|
punish_timelock,
|
|
|
|
refund_address,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_message<R: RngCore + CryptoRng>(&self, rng: &mut R) -> Message0 {
|
|
|
|
let dleq_proof_s_b = cross_curve_dleq::Proof::new(rng, &self.s_b);
|
|
|
|
|
|
|
|
Message0 {
|
|
|
|
B: self.b.public(),
|
|
|
|
S_b_monero: monero::PublicKey::from_private_key(&monero::PrivateKey {
|
|
|
|
scalar: self.s_b.into_ed25519(),
|
|
|
|
}),
|
|
|
|
S_b_bitcoin: self.s_b.into_secp256k1().into(),
|
|
|
|
dleq_proof_s_b,
|
|
|
|
v_b: self.v_b,
|
|
|
|
refund_address: self.refund_address.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn receive<W>(self, wallet: &W, msg: alice::Message0) -> anyhow::Result<State1>
|
|
|
|
where
|
|
|
|
W: BuildTxLockPsbt,
|
|
|
|
{
|
|
|
|
msg.dleq_proof_s_a.verify(
|
2020-10-21 00:28:50 -04:00
|
|
|
msg.S_a_bitcoin.clone().into(),
|
2020-09-28 02:18:50 -04:00
|
|
|
msg.S_a_monero
|
|
|
|
.point
|
|
|
|
.decompress()
|
|
|
|
.ok_or_else(|| anyhow!("S_a is not a monero curve point"))?,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let tx_lock =
|
|
|
|
bitcoin::TxLock::new(wallet, self.btc, msg.A.clone(), self.b.public()).await?;
|
|
|
|
let v = msg.v_a + self.v_b;
|
|
|
|
|
|
|
|
Ok(State1 {
|
|
|
|
A: msg.A,
|
|
|
|
b: self.b,
|
|
|
|
s_b: self.s_b,
|
|
|
|
S_a_monero: msg.S_a_monero,
|
|
|
|
S_a_bitcoin: msg.S_a_bitcoin,
|
|
|
|
v,
|
|
|
|
btc: self.btc,
|
|
|
|
xmr: self.xmr,
|
|
|
|
refund_timelock: self.refund_timelock,
|
|
|
|
punish_timelock: self.punish_timelock,
|
|
|
|
refund_address: self.refund_address,
|
|
|
|
redeem_address: msg.redeem_address,
|
|
|
|
punish_address: msg.punish_address,
|
|
|
|
tx_lock,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 18:32:25 -04:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
2020-09-28 02:18:50 -04:00
|
|
|
pub struct State1 {
|
|
|
|
A: bitcoin::PublicKey,
|
|
|
|
b: bitcoin::SecretKey,
|
|
|
|
s_b: cross_curve_dleq::Scalar,
|
|
|
|
S_a_monero: monero::PublicKey,
|
|
|
|
S_a_bitcoin: bitcoin::PublicKey,
|
|
|
|
v: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
|
|
|
refund_timelock: u32,
|
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
|
|
|
redeem_address: bitcoin::Address,
|
|
|
|
punish_address: bitcoin::Address,
|
|
|
|
tx_lock: bitcoin::TxLock,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl State1 {
|
|
|
|
pub fn next_message(&self) -> Message1 {
|
|
|
|
Message1 {
|
|
|
|
tx_lock: self.tx_lock.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive(self, msg: alice::Message1) -> Result<State2> {
|
|
|
|
let tx_cancel = TxCancel::new(
|
|
|
|
&self.tx_lock,
|
|
|
|
self.refund_timelock,
|
|
|
|
self.A.clone(),
|
|
|
|
self.b.public(),
|
|
|
|
);
|
|
|
|
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address);
|
|
|
|
|
|
|
|
bitcoin::verify_sig(&self.A, &tx_cancel.digest(), &msg.tx_cancel_sig)?;
|
|
|
|
bitcoin::verify_encsig(
|
|
|
|
self.A.clone(),
|
|
|
|
self.s_b.into_secp256k1().into(),
|
|
|
|
&tx_refund.digest(),
|
|
|
|
&msg.tx_refund_encsig,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(State2 {
|
|
|
|
A: self.A,
|
|
|
|
b: self.b,
|
|
|
|
s_b: self.s_b,
|
|
|
|
S_a_monero: self.S_a_monero,
|
|
|
|
S_a_bitcoin: self.S_a_bitcoin,
|
|
|
|
v: self.v,
|
|
|
|
btc: self.btc,
|
|
|
|
xmr: self.xmr,
|
|
|
|
refund_timelock: self.refund_timelock,
|
|
|
|
punish_timelock: self.punish_timelock,
|
|
|
|
refund_address: self.refund_address,
|
|
|
|
redeem_address: self.redeem_address,
|
|
|
|
punish_address: self.punish_address,
|
|
|
|
tx_lock: self.tx_lock,
|
|
|
|
tx_cancel_sig_a: msg.tx_cancel_sig,
|
|
|
|
tx_refund_encsig: msg.tx_refund_encsig,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 23:26:47 -05:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
2020-09-28 02:18:50 -04:00
|
|
|
pub struct State2 {
|
2020-10-12 02:17:22 -04:00
|
|
|
pub A: bitcoin::PublicKey,
|
|
|
|
pub b: bitcoin::SecretKey,
|
|
|
|
pub s_b: cross_curve_dleq::Scalar,
|
|
|
|
pub S_a_monero: monero::PublicKey,
|
|
|
|
pub S_a_bitcoin: bitcoin::PublicKey,
|
|
|
|
pub v: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
2020-10-12 02:17:22 -04:00
|
|
|
pub xmr: monero::Amount,
|
|
|
|
pub refund_timelock: u32,
|
2020-11-03 01:08:31 -05:00
|
|
|
pub punish_timelock: u32,
|
2020-10-12 02:17:22 -04:00
|
|
|
pub refund_address: bitcoin::Address,
|
|
|
|
pub redeem_address: bitcoin::Address,
|
2020-11-03 01:08:31 -05:00
|
|
|
pub punish_address: bitcoin::Address,
|
2020-10-12 02:17:22 -04:00
|
|
|
pub tx_lock: bitcoin::TxLock,
|
|
|
|
pub tx_cancel_sig_a: Signature,
|
|
|
|
pub tx_refund_encsig: EncryptedSignature,
|
2020-09-28 02:18:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl State2 {
|
|
|
|
pub fn next_message(&self) -> Message2 {
|
|
|
|
let tx_cancel = TxCancel::new(
|
|
|
|
&self.tx_lock,
|
|
|
|
self.refund_timelock,
|
|
|
|
self.A.clone(),
|
|
|
|
self.b.public(),
|
|
|
|
);
|
|
|
|
let tx_cancel_sig = self.b.sign(tx_cancel.digest());
|
|
|
|
let tx_punish =
|
|
|
|
bitcoin::TxPunish::new(&tx_cancel, &self.punish_address, self.punish_timelock);
|
|
|
|
let tx_punish_sig = self.b.sign(tx_punish.digest());
|
|
|
|
|
|
|
|
Message2 {
|
|
|
|
tx_punish_sig,
|
|
|
|
tx_cancel_sig,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
pub async fn lock_btc<W>(self, bitcoin_wallet: &W) -> Result<State3>
|
2020-09-28 02:18:50 -04:00
|
|
|
where
|
|
|
|
W: bitcoin::SignTxLock + bitcoin::BroadcastSignedTransaction,
|
|
|
|
{
|
|
|
|
let signed_tx_lock = bitcoin_wallet.sign_tx_lock(self.tx_lock.clone()).await?;
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
tracing::info!("{}", self.tx_lock.txid());
|
2020-09-28 02:18:50 -04:00
|
|
|
let _ = bitcoin_wallet
|
|
|
|
.broadcast_signed_transaction(signed_tx_lock)
|
|
|
|
.await?;
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
Ok(State3 {
|
2020-09-28 02:18:50 -04:00
|
|
|
A: self.A,
|
|
|
|
b: self.b,
|
|
|
|
s_b: self.s_b,
|
|
|
|
S_a_monero: self.S_a_monero,
|
|
|
|
S_a_bitcoin: self.S_a_bitcoin,
|
|
|
|
v: self.v,
|
|
|
|
btc: self.btc,
|
|
|
|
xmr: self.xmr,
|
|
|
|
refund_timelock: self.refund_timelock,
|
|
|
|
punish_timelock: self.punish_timelock,
|
|
|
|
refund_address: self.refund_address,
|
|
|
|
redeem_address: self.redeem_address,
|
|
|
|
punish_address: self.punish_address,
|
|
|
|
tx_lock: self.tx_lock,
|
|
|
|
tx_cancel_sig_a: self.tx_cancel_sig_a,
|
|
|
|
tx_refund_encsig: self.tx_refund_encsig,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 18:32:25 -04:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
2020-09-29 01:36:50 -04:00
|
|
|
pub struct State3 {
|
2020-09-28 02:18:50 -04:00
|
|
|
A: bitcoin::PublicKey,
|
|
|
|
b: bitcoin::SecretKey,
|
|
|
|
s_b: cross_curve_dleq::Scalar,
|
|
|
|
S_a_monero: monero::PublicKey,
|
|
|
|
S_a_bitcoin: bitcoin::PublicKey,
|
|
|
|
v: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
|
|
|
refund_timelock: u32,
|
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
|
|
|
redeem_address: bitcoin::Address,
|
|
|
|
punish_address: bitcoin::Address,
|
|
|
|
tx_lock: bitcoin::TxLock,
|
|
|
|
tx_cancel_sig_a: Signature,
|
|
|
|
tx_refund_encsig: EncryptedSignature,
|
|
|
|
}
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
impl State3 {
|
|
|
|
pub async fn watch_for_lock_xmr<W>(self, xmr_wallet: &W, msg: alice::Message2) -> Result<State4>
|
2020-09-28 02:18:50 -04:00
|
|
|
where
|
2020-10-14 22:10:31 -04:00
|
|
|
W: monero::WatchForTransfer,
|
2020-09-28 02:18:50 -04:00
|
|
|
{
|
|
|
|
let S_b_monero = monero::PublicKey::from_private_key(&monero::PrivateKey::from_scalar(
|
|
|
|
self.s_b.into_ed25519(),
|
|
|
|
));
|
|
|
|
let S = self.S_a_monero + S_b_monero;
|
|
|
|
|
|
|
|
xmr_wallet
|
2020-10-14 22:10:31 -04:00
|
|
|
.watch_for_transfer(
|
|
|
|
S,
|
|
|
|
self.v.public(),
|
|
|
|
msg.tx_lock_proof,
|
|
|
|
self.xmr,
|
|
|
|
monero::MIN_CONFIRMATIONS,
|
|
|
|
)
|
2020-09-28 02:18:50 -04:00
|
|
|
.await?;
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
Ok(State4 {
|
2020-09-28 02:18:50 -04:00
|
|
|
A: self.A,
|
|
|
|
b: self.b,
|
|
|
|
s_b: self.s_b,
|
|
|
|
S_a_monero: self.S_a_monero,
|
|
|
|
S_a_bitcoin: self.S_a_bitcoin,
|
|
|
|
v: self.v,
|
|
|
|
btc: self.btc,
|
|
|
|
xmr: self.xmr,
|
|
|
|
refund_timelock: self.refund_timelock,
|
|
|
|
punish_timelock: self.punish_timelock,
|
|
|
|
refund_address: self.refund_address,
|
|
|
|
redeem_address: self.redeem_address,
|
|
|
|
punish_address: self.punish_address,
|
|
|
|
tx_lock: self.tx_lock,
|
|
|
|
tx_cancel_sig_a: self.tx_cancel_sig_a,
|
|
|
|
tx_refund_encsig: self.tx_refund_encsig,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(
|
|
|
|
&self,
|
|
|
|
bitcoin_wallet: &W,
|
|
|
|
) -> Result<()> {
|
|
|
|
let tx_cancel = bitcoin::TxCancel::new(
|
|
|
|
&self.tx_lock,
|
|
|
|
self.refund_timelock,
|
|
|
|
self.A.clone(),
|
|
|
|
self.b.public(),
|
|
|
|
);
|
|
|
|
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address);
|
|
|
|
|
|
|
|
{
|
|
|
|
let sig_b = self.b.sign(tx_cancel.digest());
|
|
|
|
let sig_a = self.tx_cancel_sig_a.clone();
|
|
|
|
|
|
|
|
let signed_tx_cancel = tx_cancel.clone().add_signatures(
|
|
|
|
&self.tx_lock,
|
|
|
|
(self.A.clone(), sig_a),
|
|
|
|
(self.b.public(), sig_b),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let _ = bitcoin_wallet
|
|
|
|
.broadcast_signed_transaction(signed_tx_cancel)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default();
|
|
|
|
|
|
|
|
let sig_b = self.b.sign(tx_refund.digest());
|
|
|
|
let sig_a = adaptor
|
|
|
|
.decrypt_signature(&self.s_b.into_secp256k1(), self.tx_refund_encsig.clone());
|
|
|
|
|
|
|
|
let signed_tx_refund = tx_refund.add_signatures(
|
|
|
|
&tx_cancel.clone(),
|
|
|
|
(self.A.clone(), sig_a),
|
|
|
|
(self.b.public(), sig_b),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let _ = bitcoin_wallet
|
|
|
|
.broadcast_signed_transaction(signed_tx_refund)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
|
|
|
self.tx_lock.txid()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-11 19:06:34 -05:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
2020-09-29 01:36:50 -04:00
|
|
|
pub struct State4 {
|
2020-11-11 19:06:34 -05:00
|
|
|
pub A: bitcoin::PublicKey,
|
|
|
|
pub b: bitcoin::SecretKey,
|
2020-09-28 02:18:50 -04:00
|
|
|
s_b: cross_curve_dleq::Scalar,
|
|
|
|
S_a_monero: monero::PublicKey,
|
2020-11-11 19:06:34 -05:00
|
|
|
pub S_a_bitcoin: bitcoin::PublicKey,
|
2020-09-28 02:18:50 -04:00
|
|
|
v: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
2020-11-11 19:06:34 -05:00
|
|
|
pub refund_timelock: u32,
|
2020-09-28 02:18:50 -04:00
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
2020-11-11 19:06:34 -05:00
|
|
|
pub redeem_address: bitcoin::Address,
|
2020-09-28 02:18:50 -04:00
|
|
|
punish_address: bitcoin::Address,
|
2020-11-11 19:06:34 -05:00
|
|
|
pub tx_lock: bitcoin::TxLock,
|
2020-09-28 02:18:50 -04:00
|
|
|
tx_cancel_sig_a: Signature,
|
|
|
|
tx_refund_encsig: EncryptedSignature,
|
|
|
|
}
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
impl State4 {
|
2020-09-28 02:18:50 -04:00
|
|
|
pub fn next_message(&self) -> Message3 {
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
|
|
|
let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin.clone(), tx_redeem.digest());
|
|
|
|
|
|
|
|
Message3 { tx_redeem_encsig }
|
|
|
|
}
|
|
|
|
|
2020-11-11 19:06:34 -05:00
|
|
|
pub fn tx_redeem_encsig(&self) -> EncryptedSignature {
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
|
|
|
self.b.encsign(self.S_a_bitcoin.clone(), tx_redeem.digest())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn check_for_tx_cancel<W>(&self, bitcoin_wallet: &W) -> Result<Transaction>
|
|
|
|
where
|
|
|
|
W: GetRawTransaction,
|
|
|
|
{
|
|
|
|
let tx_cancel = bitcoin::TxCancel::new(
|
|
|
|
&self.tx_lock,
|
|
|
|
self.refund_timelock,
|
|
|
|
self.A.clone(),
|
|
|
|
self.b.public(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// todo: check if this is correct
|
|
|
|
let sig_a = self.tx_cancel_sig_a.clone();
|
|
|
|
let sig_b = self.b.sign(tx_cancel.digest());
|
|
|
|
|
|
|
|
let tx_cancel = tx_cancel
|
|
|
|
.clone()
|
|
|
|
.add_signatures(
|
|
|
|
&self.tx_lock,
|
|
|
|
(self.A.clone(), sig_a),
|
|
|
|
(self.b.public(), sig_b),
|
|
|
|
)
|
|
|
|
.expect(
|
|
|
|
"sig_{a,b} to be valid signatures for
|
|
|
|
tx_cancel",
|
|
|
|
);
|
|
|
|
|
|
|
|
let tx = bitcoin_wallet.get_raw_transaction(tx_cancel.txid()).await?;
|
|
|
|
|
|
|
|
Ok(tx)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn submit_tx_cancel<W>(&self, bitcoin_wallet: &W) -> Result<Txid>
|
|
|
|
where
|
|
|
|
W: BroadcastSignedTransaction,
|
|
|
|
{
|
|
|
|
let tx_cancel = bitcoin::TxCancel::new(
|
|
|
|
&self.tx_lock,
|
|
|
|
self.refund_timelock,
|
|
|
|
self.A.clone(),
|
|
|
|
self.b.public(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// todo: check if this is correct
|
|
|
|
let sig_a = self.tx_cancel_sig_a.clone();
|
|
|
|
let sig_b = self.b.sign(tx_cancel.digest());
|
|
|
|
|
|
|
|
let tx_cancel = tx_cancel
|
|
|
|
.clone()
|
|
|
|
.add_signatures(
|
|
|
|
&self.tx_lock,
|
|
|
|
(self.A.clone(), sig_a),
|
|
|
|
(self.b.public(), sig_b),
|
|
|
|
)
|
|
|
|
.expect(
|
|
|
|
"sig_{a,b} to be valid signatures for
|
|
|
|
tx_cancel",
|
|
|
|
);
|
|
|
|
|
|
|
|
let tx_id = bitcoin_wallet
|
|
|
|
.broadcast_signed_transaction(tx_cancel)
|
|
|
|
.await?;
|
|
|
|
Ok(tx_id)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn watch_for_redeem_btc<W>(&self, bitcoin_wallet: &W) -> Result<State5>
|
2020-09-28 02:18:50 -04:00
|
|
|
where
|
2020-10-07 19:53:30 -04:00
|
|
|
W: WatchForRawTransaction,
|
2020-09-28 02:18:50 -04:00
|
|
|
{
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
|
|
|
let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin.clone(), tx_redeem.digest());
|
|
|
|
|
2020-10-07 19:53:30 -04:00
|
|
|
let tx_redeem_candidate = bitcoin_wallet
|
|
|
|
.watch_for_raw_transaction(tx_redeem.txid())
|
2020-10-15 06:17:42 -04:00
|
|
|
.await;
|
2020-09-28 02:18:50 -04:00
|
|
|
|
|
|
|
let tx_redeem_sig =
|
|
|
|
tx_redeem.extract_signature_by_key(tx_redeem_candidate, self.b.public())?;
|
|
|
|
let s_a = bitcoin::recover(self.S_a_bitcoin.clone(), tx_redeem_sig, tx_redeem_encsig)?;
|
2020-11-08 19:07:06 -05:00
|
|
|
let s_a = monero::private_key_from_secp256k1_scalar(s_a.into());
|
2020-09-28 02:18:50 -04:00
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
Ok(State5 {
|
2020-11-11 19:06:34 -05:00
|
|
|
A: self.A.clone(),
|
|
|
|
b: self.b.clone(),
|
2020-09-28 02:18:50 -04:00
|
|
|
s_a,
|
|
|
|
s_b: self.s_b,
|
|
|
|
S_a_monero: self.S_a_monero,
|
2020-11-11 19:06:34 -05:00
|
|
|
S_a_bitcoin: self.S_a_bitcoin.clone(),
|
2020-09-28 02:18:50 -04:00
|
|
|
v: self.v,
|
|
|
|
btc: self.btc,
|
|
|
|
xmr: self.xmr,
|
|
|
|
refund_timelock: self.refund_timelock,
|
|
|
|
punish_timelock: self.punish_timelock,
|
2020-11-11 19:06:34 -05:00
|
|
|
refund_address: self.refund_address.clone(),
|
|
|
|
redeem_address: self.redeem_address.clone(),
|
|
|
|
punish_address: self.punish_address.clone(),
|
|
|
|
tx_lock: self.tx_lock.clone(),
|
|
|
|
tx_refund_encsig: self.tx_refund_encsig.clone(),
|
|
|
|
tx_cancel_sig: self.tx_cancel_sig_a.clone(),
|
2020-09-28 02:18:50 -04:00
|
|
|
})
|
|
|
|
}
|
2020-11-11 19:06:34 -05:00
|
|
|
|
|
|
|
pub async fn wait_for_t1<W>(&self, bitcoin_wallet: &W) -> Result<()>
|
|
|
|
where
|
|
|
|
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
|
|
|
|
{
|
|
|
|
let tx_id = self.tx_lock.txid().clone();
|
|
|
|
let tx_lock_height = bitcoin_wallet.transaction_block_height(tx_id).await;
|
|
|
|
|
|
|
|
let t1_timeout =
|
|
|
|
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + self.refund_timelock);
|
|
|
|
t1_timeout.await;
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-28 02:18:50 -04:00
|
|
|
}
|
|
|
|
|
2020-10-13 18:32:25 -04:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
2020-09-29 01:36:50 -04:00
|
|
|
pub struct State5 {
|
2020-09-28 02:18:50 -04:00
|
|
|
A: bitcoin::PublicKey,
|
|
|
|
b: bitcoin::SecretKey,
|
2020-10-13 18:32:25 -04:00
|
|
|
#[serde(with = "monero_private_key")]
|
2020-09-28 02:18:50 -04:00
|
|
|
s_a: monero::PrivateKey,
|
|
|
|
s_b: cross_curve_dleq::Scalar,
|
|
|
|
S_a_monero: monero::PublicKey,
|
|
|
|
S_a_bitcoin: bitcoin::PublicKey,
|
|
|
|
v: monero::PrivateViewKey,
|
2020-10-22 00:06:05 -04:00
|
|
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
2020-09-28 02:18:50 -04:00
|
|
|
btc: bitcoin::Amount,
|
|
|
|
xmr: monero::Amount,
|
|
|
|
refund_timelock: u32,
|
|
|
|
punish_timelock: u32,
|
|
|
|
refund_address: bitcoin::Address,
|
|
|
|
redeem_address: bitcoin::Address,
|
|
|
|
punish_address: bitcoin::Address,
|
|
|
|
tx_lock: bitcoin::TxLock,
|
|
|
|
tx_refund_encsig: EncryptedSignature,
|
|
|
|
tx_cancel_sig: Signature,
|
|
|
|
}
|
|
|
|
|
2020-09-29 01:36:50 -04:00
|
|
|
impl State5 {
|
2020-09-28 02:18:50 -04:00
|
|
|
pub async fn claim_xmr<W>(&self, monero_wallet: &W) -> Result<()>
|
|
|
|
where
|
2020-10-07 20:27:54 -04:00
|
|
|
W: monero::CreateWalletForOutput,
|
2020-09-28 02:18:50 -04:00
|
|
|
{
|
|
|
|
let s_b = monero::PrivateKey {
|
|
|
|
scalar: self.s_b.into_ed25519(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let s = self.s_a + s_b;
|
|
|
|
|
|
|
|
// NOTE: This actually generates and opens a new wallet, closing the currently
|
|
|
|
// open one.
|
2020-10-07 20:27:54 -04:00
|
|
|
monero_wallet
|
|
|
|
.create_and_load_wallet_for_output(s, self.v)
|
|
|
|
.await?;
|
2020-09-28 02:18:50 -04:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-29 01:36:50 -04:00
|
|
|
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
|
|
|
self.tx_lock.txid()
|
|
|
|
}
|
2020-09-28 02:18:50 -04:00
|
|
|
}
|
2020-11-11 19:06:34 -05:00
|
|
|
|
|
|
|
/// Watch for the refund transaction on the blockchain. Watch until t2 has
|
|
|
|
/// elapsed.
|
|
|
|
pub async fn watch_for_refund_btc<W>(state: State5, bitcoin_wallet: &W) -> Result<()>
|
|
|
|
where
|
|
|
|
W: WatchForRawTransaction,
|
|
|
|
{
|
|
|
|
let tx_cancel = bitcoin::TxCancel::new(
|
|
|
|
&state.tx_lock,
|
|
|
|
state.refund_timelock,
|
|
|
|
state.A.clone(),
|
|
|
|
state.b.public(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &state.refund_address);
|
|
|
|
|
|
|
|
let tx_refund_watcher = bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Watch for refund transaction on the blockchain
|
|
|
|
pub async fn watch_for_redeem_btc<W>(state: State4, bitcoin_wallet: &W) -> Result<State5>
|
|
|
|
where
|
|
|
|
W: WatchForRawTransaction,
|
|
|
|
{
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&state.tx_lock, &state.redeem_address);
|
|
|
|
let tx_redeem_encsig = state
|
|
|
|
.b
|
|
|
|
.encsign(state.S_a_bitcoin.clone(), tx_redeem.digest());
|
|
|
|
|
|
|
|
let tx_redeem_candidate = bitcoin_wallet
|
|
|
|
.watch_for_raw_transaction(tx_redeem.txid())
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let tx_redeem_sig =
|
|
|
|
tx_redeem.extract_signature_by_key(tx_redeem_candidate, state.b.public())?;
|
|
|
|
let s_a = bitcoin::recover(state.S_a_bitcoin.clone(), tx_redeem_sig, tx_redeem_encsig)?;
|
|
|
|
let s_a = monero::private_key_from_secp256k1_scalar(s_a.into());
|
|
|
|
|
|
|
|
Ok(State5 {
|
|
|
|
A: state.A.clone(),
|
|
|
|
b: state.b.clone(),
|
|
|
|
s_a,
|
|
|
|
s_b: state.s_b,
|
|
|
|
S_a_monero: state.S_a_monero,
|
|
|
|
S_a_bitcoin: state.S_a_bitcoin.clone(),
|
|
|
|
v: state.v,
|
|
|
|
btc: state.btc,
|
|
|
|
xmr: state.xmr,
|
|
|
|
refund_timelock: state.refund_timelock,
|
|
|
|
punish_timelock: state.punish_timelock,
|
|
|
|
refund_address: state.refund_address.clone(),
|
|
|
|
redeem_address: state.redeem_address.clone(),
|
|
|
|
punish_address: state.punish_address.clone(),
|
|
|
|
tx_lock: state.tx_lock.clone(),
|
|
|
|
tx_refund_encsig: state.tx_refund_encsig.clone(),
|
|
|
|
tx_cancel_sig: state.tx_cancel_sig_a.clone(),
|
|
|
|
})
|
|
|
|
}
|