You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
596 lines
19 KiB
Rust
596 lines
19 KiB
Rust
4 years ago
|
use ::bitcoin::{Transaction, Txid};
|
||
4 years ago
|
use anyhow::{anyhow, Result};
|
||
|
use ecdsa_fun::{
|
||
|
adaptor::{Adaptor, EncryptedSignature},
|
||
|
nonce::Deterministic,
|
||
|
Signature,
|
||
|
};
|
||
|
use rand::{CryptoRng, RngCore};
|
||
4 years ago
|
use serde::{Deserialize, Serialize};
|
||
4 years ago
|
use sha2::Sha256;
|
||
4 years ago
|
|
||
4 years ago
|
use crate::{
|
||
4 years ago
|
bitcoin::{
|
||
4 years ago
|
self, current_epoch, timelocks::Timelock, wait_for_cancel_timelock_to_expire,
|
||
|
BroadcastSignedTransaction, BuildTxLockPsbt, GetBlockHeight, GetRawTransaction, Network,
|
||
|
TransactionBlockHeight, TxCancel, WatchForRawTransaction,
|
||
4 years ago
|
},
|
||
4 years ago
|
monero,
|
||
|
protocol::{alice, bob},
|
||
|
serde::monero_private_key,
|
||
|
ExpiredTimelocks,
|
||
4 years ago
|
};
|
||
4 years ago
|
|
||
4 years ago
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||
4 years ago
|
pub struct State0 {
|
||
|
b: bitcoin::SecretKey,
|
||
|
s_b: cross_curve_dleq::Scalar,
|
||
|
v_b: monero::PrivateViewKey,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
refund_address: bitcoin::Address,
|
||
4 years ago
|
min_monero_confirmations: u32,
|
||
4 years ago
|
}
|
||
|
|
||
|
impl State0 {
|
||
|
pub fn new<R: RngCore + CryptoRng>(
|
||
|
rng: &mut R,
|
||
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
refund_address: bitcoin::Address,
|
||
4 years ago
|
min_monero_confirmations: u32,
|
||
4 years ago
|
) -> 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,
|
||
4 years ago
|
cancel_timelock,
|
||
4 years ago
|
punish_timelock,
|
||
|
refund_address,
|
||
4 years ago
|
min_monero_confirmations,
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
pub fn next_message<R: RngCore + CryptoRng>(&self, rng: &mut R) -> bob::Message0 {
|
||
4 years ago
|
let dleq_proof_s_b = cross_curve_dleq::Proof::new(rng, &self.s_b);
|
||
|
|
||
4 years ago
|
bob::Message0 {
|
||
4 years ago
|
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
|
||
4 years ago
|
W: BuildTxLockPsbt + Network,
|
||
4 years ago
|
{
|
||
|
msg.dleq_proof_s_a.verify(
|
||
4 years ago
|
msg.S_a_bitcoin.clone().into(),
|
||
4 years ago
|
msg.S_a_monero
|
||
|
.point
|
||
|
.decompress()
|
||
|
.ok_or_else(|| anyhow!("S_a is not a monero curve point"))?,
|
||
|
)?;
|
||
|
|
||
4 years ago
|
let tx_lock = bitcoin::TxLock::new(wallet, self.btc, msg.A, self.b.public()).await?;
|
||
4 years ago
|
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,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
punish_timelock: self.punish_timelock,
|
||
|
refund_address: self.refund_address,
|
||
|
redeem_address: msg.redeem_address,
|
||
|
punish_address: msg.punish_address,
|
||
|
tx_lock,
|
||
4 years ago
|
min_monero_confirmations: self.min_monero_confirmations,
|
||
4 years ago
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
#[derive(Debug, Deserialize, Serialize)]
|
||
4 years ago
|
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,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
refund_address: bitcoin::Address,
|
||
|
redeem_address: bitcoin::Address,
|
||
|
punish_address: bitcoin::Address,
|
||
|
tx_lock: bitcoin::TxLock,
|
||
4 years ago
|
min_monero_confirmations: u32,
|
||
4 years ago
|
}
|
||
|
|
||
|
impl State1 {
|
||
4 years ago
|
pub fn next_message(&self) -> bob::Message1 {
|
||
|
bob::Message1 {
|
||
4 years ago
|
tx_lock: self.tx_lock.clone(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn receive(self, msg: alice::Message1) -> Result<State2> {
|
||
4 years ago
|
let tx_cancel = TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||
4 years ago
|
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(
|
||
4 years ago
|
self.A,
|
||
4 years ago
|
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,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
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,
|
||
4 years ago
|
min_monero_confirmations: self.min_monero_confirmations,
|
||
4 years ago
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||
4 years ago
|
pub struct State2 {
|
||
4 years ago
|
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,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
4 years ago
|
pub xmr: monero::Amount,
|
||
4 years ago
|
pub cancel_timelock: Timelock,
|
||
|
pub punish_timelock: Timelock,
|
||
4 years ago
|
pub refund_address: bitcoin::Address,
|
||
|
pub redeem_address: bitcoin::Address,
|
||
4 years ago
|
pub punish_address: bitcoin::Address,
|
||
4 years ago
|
pub tx_lock: bitcoin::TxLock,
|
||
|
pub tx_cancel_sig_a: Signature,
|
||
|
pub tx_refund_encsig: EncryptedSignature,
|
||
4 years ago
|
pub min_monero_confirmations: u32,
|
||
4 years ago
|
}
|
||
|
|
||
|
impl State2 {
|
||
4 years ago
|
pub fn next_message(&self) -> bob::Message2 {
|
||
4 years ago
|
let tx_cancel = TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||
4 years ago
|
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());
|
||
|
|
||
4 years ago
|
bob::Message2 {
|
||
4 years ago
|
tx_punish_sig,
|
||
|
tx_cancel_sig,
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
pub async fn lock_btc<W>(self, bitcoin_wallet: &W) -> Result<State3>
|
||
4 years ago
|
where
|
||
|
W: bitcoin::SignTxLock + bitcoin::BroadcastSignedTransaction,
|
||
|
{
|
||
|
let signed_tx_lock = bitcoin_wallet.sign_tx_lock(self.tx_lock.clone()).await?;
|
||
|
|
||
4 years ago
|
tracing::info!("{}", self.tx_lock.txid());
|
||
4 years ago
|
let _ = bitcoin_wallet
|
||
|
.broadcast_signed_transaction(signed_tx_lock)
|
||
|
.await?;
|
||
|
|
||
4 years ago
|
Ok(State3 {
|
||
4 years ago
|
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,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
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,
|
||
4 years ago
|
min_monero_confirmations: self.min_monero_confirmations,
|
||
4 years ago
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
4 years ago
|
pub struct State3 {
|
||
4 years ago
|
pub A: bitcoin::PublicKey,
|
||
|
pub b: bitcoin::SecretKey,
|
||
|
pub s_b: cross_curve_dleq::Scalar,
|
||
4 years ago
|
S_a_monero: monero::PublicKey,
|
||
|
S_a_bitcoin: bitcoin::PublicKey,
|
||
|
v: monero::PrivateViewKey,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
pub cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
pub refund_address: bitcoin::Address,
|
||
4 years ago
|
redeem_address: bitcoin::Address,
|
||
|
punish_address: bitcoin::Address,
|
||
4 years ago
|
pub tx_lock: bitcoin::TxLock,
|
||
|
pub tx_cancel_sig_a: Signature,
|
||
|
pub tx_refund_encsig: EncryptedSignature,
|
||
4 years ago
|
pub min_monero_confirmations: u32,
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl State3 {
|
||
|
pub async fn watch_for_lock_xmr<W>(self, xmr_wallet: &W, msg: alice::Message2) -> Result<State4>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: monero::WatchForTransfer,
|
||
4 years ago
|
{
|
||
|
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
|
||
4 years ago
|
.watch_for_transfer(
|
||
|
S,
|
||
|
self.v.public(),
|
||
|
msg.tx_lock_proof,
|
||
|
self.xmr,
|
||
4 years ago
|
self.min_monero_confirmations,
|
||
4 years ago
|
)
|
||
4 years ago
|
.await?;
|
||
|
|
||
4 years ago
|
Ok(State4 {
|
||
4 years ago
|
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,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
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,
|
||
|
})
|
||
|
}
|
||
|
|
||
4 years ago
|
pub async fn wait_for_cancel_timelock_to_expire<W>(&self, bitcoin_wallet: &W) -> Result<()>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||
4 years ago
|
{
|
||
4 years ago
|
wait_for_cancel_timelock_to_expire(
|
||
|
bitcoin_wallet,
|
||
|
self.cancel_timelock,
|
||
|
self.tx_lock.txid(),
|
||
|
)
|
||
|
.await
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
pub fn state4(&self) -> State4 {
|
||
4 years ago
|
State4 {
|
||
|
A: self.A,
|
||
|
b: self.b.clone(),
|
||
|
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,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
punish_timelock: self.punish_timelock,
|
||
|
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_cancel_sig_a: self.tx_cancel_sig_a.clone(),
|
||
|
tx_refund_encsig: self.tx_refund_encsig.clone(),
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
||
|
self.tx_lock.txid()
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
pub async fn current_epoch<W>(&self, bitcoin_wallet: &W) -> Result<ExpiredTimelocks>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||
4 years ago
|
{
|
||
4 years ago
|
current_epoch(
|
||
4 years ago
|
bitcoin_wallet,
|
||
4 years ago
|
self.cancel_timelock,
|
||
4 years ago
|
self.punish_timelock,
|
||
|
self.tx_lock.txid(),
|
||
|
)
|
||
|
.await
|
||
|
}
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||
4 years ago
|
pub struct State4 {
|
||
4 years ago
|
pub A: bitcoin::PublicKey,
|
||
|
pub b: bitcoin::SecretKey,
|
||
4 years ago
|
pub s_b: cross_curve_dleq::Scalar,
|
||
4 years ago
|
S_a_monero: monero::PublicKey,
|
||
4 years ago
|
pub S_a_bitcoin: bitcoin::PublicKey,
|
||
4 years ago
|
v: monero::PrivateViewKey,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
pub cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
pub refund_address: bitcoin::Address,
|
||
4 years ago
|
pub redeem_address: bitcoin::Address,
|
||
4 years ago
|
punish_address: bitcoin::Address,
|
||
4 years ago
|
pub tx_lock: bitcoin::TxLock,
|
||
4 years ago
|
pub tx_cancel_sig_a: Signature,
|
||
|
pub tx_refund_encsig: EncryptedSignature,
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl State4 {
|
||
4 years ago
|
pub fn next_message(&self) -> bob::Message3 {
|
||
4 years ago
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
||
4 years ago
|
let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin, tx_redeem.digest());
|
||
4 years ago
|
|
||
4 years ago
|
bob::Message3 { tx_redeem_encsig }
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
pub fn tx_redeem_encsig(&self) -> EncryptedSignature {
|
||
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
||
4 years ago
|
self.b.encsign(self.S_a_bitcoin, tx_redeem.digest())
|
||
4 years ago
|
}
|
||
|
|
||
|
pub async fn check_for_tx_cancel<W>(&self, bitcoin_wallet: &W) -> Result<Transaction>
|
||
|
where
|
||
|
W: GetRawTransaction,
|
||
|
{
|
||
4 years ago
|
let tx_cancel =
|
||
4 years ago
|
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||
4 years ago
|
|
||
|
let sig_a = self.tx_cancel_sig_a.clone();
|
||
|
let sig_b = self.b.sign(tx_cancel.digest());
|
||
|
|
||
|
let tx_cancel = tx_cancel
|
||
|
.clone()
|
||
4 years ago
|
.add_signatures(&self.tx_lock, (self.A, sig_a), (self.b.public(), sig_b))
|
||
4 years ago
|
.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,
|
||
|
{
|
||
4 years ago
|
let tx_cancel =
|
||
4 years ago
|
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||
4 years ago
|
|
||
|
let sig_a = self.tx_cancel_sig_a.clone();
|
||
|
let sig_b = self.b.sign(tx_cancel.digest());
|
||
|
|
||
|
let tx_cancel = tx_cancel
|
||
|
.clone()
|
||
4 years ago
|
.add_signatures(&self.tx_lock, (self.A, sig_a), (self.b.public(), sig_b))
|
||
4 years ago
|
.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>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: WatchForRawTransaction,
|
||
4 years ago
|
{
|
||
|
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
|
||
4 years ago
|
let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin, tx_redeem.digest());
|
||
4 years ago
|
|
||
4 years ago
|
let tx_redeem_candidate = bitcoin_wallet
|
||
|
.watch_for_raw_transaction(tx_redeem.txid())
|
||
4 years ago
|
.await;
|
||
4 years ago
|
|
||
|
let tx_redeem_sig =
|
||
|
tx_redeem.extract_signature_by_key(tx_redeem_candidate, self.b.public())?;
|
||
4 years ago
|
let s_a = bitcoin::recover(self.S_a_bitcoin, tx_redeem_sig, tx_redeem_encsig)?;
|
||
4 years ago
|
let s_a = monero::private_key_from_secp256k1_scalar(s_a.into());
|
||
4 years ago
|
|
||
4 years ago
|
Ok(State5 {
|
||
4 years ago
|
A: self.A,
|
||
4 years ago
|
b: self.b.clone(),
|
||
4 years ago
|
s_a,
|
||
|
s_b: self.s_b,
|
||
|
S_a_monero: self.S_a_monero,
|
||
4 years ago
|
S_a_bitcoin: self.S_a_bitcoin,
|
||
4 years ago
|
v: self.v,
|
||
|
btc: self.btc,
|
||
|
xmr: self.xmr,
|
||
4 years ago
|
cancel_timelock: self.cancel_timelock,
|
||
4 years ago
|
punish_timelock: self.punish_timelock,
|
||
4 years ago
|
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(),
|
||
4 years ago
|
})
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
pub async fn wait_for_cancel_timelock_to_expire<W>(&self, bitcoin_wallet: &W) -> Result<()>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||
4 years ago
|
{
|
||
4 years ago
|
wait_for_cancel_timelock_to_expire(
|
||
|
bitcoin_wallet,
|
||
|
self.cancel_timelock,
|
||
|
self.tx_lock.txid(),
|
||
|
)
|
||
|
.await
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
pub async fn expired_timelock<W>(&self, bitcoin_wallet: &W) -> Result<ExpiredTimelocks>
|
||
4 years ago
|
where
|
||
4 years ago
|
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||
4 years ago
|
{
|
||
4 years ago
|
current_epoch(
|
||
4 years ago
|
bitcoin_wallet,
|
||
4 years ago
|
self.cancel_timelock,
|
||
4 years ago
|
self.punish_timelock,
|
||
|
self.tx_lock.txid(),
|
||
|
)
|
||
|
.await
|
||
4 years ago
|
}
|
||
|
|
||
|
pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(
|
||
|
&self,
|
||
|
bitcoin_wallet: &W,
|
||
|
) -> Result<()> {
|
||
|
let tx_cancel =
|
||
4 years ago
|
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||
4 years ago
|
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, 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, sig_a),
|
||
|
(self.b.public(), sig_b),
|
||
|
)?;
|
||
|
|
||
|
let _ = bitcoin_wallet
|
||
|
.broadcast_signed_transaction(signed_tx_refund)
|
||
|
.await?;
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
4 years ago
|
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
||
|
self.tx_lock.txid()
|
||
|
}
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||
4 years ago
|
pub struct State5 {
|
||
4 years ago
|
A: bitcoin::PublicKey,
|
||
4 years ago
|
pub b: bitcoin::SecretKey,
|
||
4 years ago
|
#[serde(with = "monero_private_key")]
|
||
4 years ago
|
s_a: monero::PrivateKey,
|
||
4 years ago
|
pub s_b: cross_curve_dleq::Scalar,
|
||
4 years ago
|
S_a_monero: monero::PublicKey,
|
||
4 years ago
|
pub S_a_bitcoin: bitcoin::PublicKey,
|
||
|
pub v: monero::PrivateViewKey,
|
||
4 years ago
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||
4 years ago
|
btc: bitcoin::Amount,
|
||
|
xmr: monero::Amount,
|
||
4 years ago
|
cancel_timelock: Timelock,
|
||
|
punish_timelock: Timelock,
|
||
4 years ago
|
refund_address: bitcoin::Address,
|
||
4 years ago
|
pub redeem_address: bitcoin::Address,
|
||
4 years ago
|
punish_address: bitcoin::Address,
|
||
4 years ago
|
pub tx_lock: bitcoin::TxLock,
|
||
4 years ago
|
tx_refund_encsig: EncryptedSignature,
|
||
|
tx_cancel_sig: Signature,
|
||
|
}
|
||
|
|
||
4 years ago
|
impl State5 {
|
||
4 years ago
|
pub async fn claim_xmr<W>(&self, monero_wallet: &W) -> Result<()>
|
||
|
where
|
||
4 years ago
|
W: monero::CreateWalletForOutput,
|
||
4 years ago
|
{
|
||
|
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.
|
||
4 years ago
|
monero_wallet
|
||
|
.create_and_load_wallet_for_output(s, self.v)
|
||
|
.await?;
|
||
4 years ago
|
|
||
|
Ok(())
|
||
|
}
|
||
4 years ago
|
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
||
|
self.tx_lock.txid()
|
||
|
}
|
||
4 years ago
|
}
|