You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
4.7 KiB
126 lines
4.7 KiB
use crate::bitcoin::{self};
|
|
use crate::database::{Database, Swap};
|
|
use crate::monero;
|
|
use crate::protocol::alice::AliceState;
|
|
use anyhow::{bail, Result};
|
|
use libp2p::PeerId;
|
|
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum Error {
|
|
// Errors indicating the the swap can *currently* not be refunded but might be later
|
|
#[error("Swap is not in a cancelled state. Make sure to cancel the swap before trying to refund or use --force.")]
|
|
SwapNotCancelled,
|
|
#[error(
|
|
"Counterparty {0} did not refund the BTC yet. You can try again later or try to punish."
|
|
)]
|
|
RefundTransactionNotPublishedYet(PeerId),
|
|
|
|
// Errors indicating that the swap cannot be refunded because because it is in a abort/final
|
|
// state
|
|
#[error("Swa is in state {0} where no XMR was locked. Try aborting instead.")]
|
|
NoXmrLocked(AliceState),
|
|
#[error("Swap is in state {0} which is not refundable")]
|
|
SwapNotRefundable(AliceState),
|
|
}
|
|
|
|
pub async fn refund(
|
|
swap_id: Uuid,
|
|
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
|
monero_wallet: Arc<monero::Wallet>,
|
|
db: Arc<Database>,
|
|
force: bool,
|
|
) -> Result<Result<AliceState, Error>> {
|
|
let state = db.get_state(swap_id)?.try_into_alice()?.into();
|
|
|
|
let (monero_wallet_restore_blockheight, transfer_proof, state3) = if force {
|
|
match state {
|
|
|
|
// In case no XMR has been locked, move to Safely Aborted
|
|
AliceState::Started { .. }
|
|
| AliceState::BtcLocked { .. } => bail!(Error::NoXmrLocked(state)),
|
|
|
|
// Refund potentially possible (no knowledge of cancel transaction)
|
|
AliceState::XmrLockTransactionSent { monero_wallet_restore_blockheight, transfer_proof, state3, }
|
|
| AliceState::XmrLocked { monero_wallet_restore_blockheight, transfer_proof, state3 }
|
|
| AliceState::XmrLockTransferProofSent { monero_wallet_restore_blockheight, transfer_proof, state3 }
|
|
| AliceState::EncSigLearned { monero_wallet_restore_blockheight, transfer_proof, state3, .. }
|
|
| AliceState::CancelTimelockExpired { monero_wallet_restore_blockheight, transfer_proof, state3 }
|
|
|
|
// Refund possible due to cancel transaction already being published
|
|
| AliceState::BtcCancelled { monero_wallet_restore_blockheight, transfer_proof, state3 }
|
|
| AliceState::BtcRefunded { monero_wallet_restore_blockheight, transfer_proof, state3, .. }
|
|
| AliceState::BtcPunishable { monero_wallet_restore_blockheight, transfer_proof, state3, .. } => {
|
|
(monero_wallet_restore_blockheight, transfer_proof, state3)
|
|
}
|
|
|
|
// Alice already in final state
|
|
AliceState::BtcRedeemed
|
|
| AliceState::XmrRefunded
|
|
| AliceState::BtcPunished
|
|
| AliceState::SafelyAborted => bail!(Error::SwapNotRefundable(state)),
|
|
}
|
|
} else {
|
|
match state {
|
|
AliceState::Started { .. } | AliceState::BtcLocked { .. } => {
|
|
bail!(Error::NoXmrLocked(state))
|
|
}
|
|
|
|
AliceState::BtcCancelled {
|
|
monero_wallet_restore_blockheight,
|
|
transfer_proof,
|
|
state3,
|
|
}
|
|
| AliceState::BtcRefunded {
|
|
monero_wallet_restore_blockheight,
|
|
transfer_proof,
|
|
state3,
|
|
..
|
|
}
|
|
| AliceState::BtcPunishable {
|
|
monero_wallet_restore_blockheight,
|
|
transfer_proof,
|
|
state3,
|
|
..
|
|
} => (monero_wallet_restore_blockheight, transfer_proof, state3),
|
|
|
|
AliceState::BtcRedeemed
|
|
| AliceState::XmrRefunded
|
|
| AliceState::BtcPunished
|
|
| AliceState::SafelyAborted => bail!(Error::SwapNotRefundable(state)),
|
|
|
|
_ => return Ok(Err(Error::SwapNotCancelled)),
|
|
}
|
|
};
|
|
|
|
tracing::info!(%swap_id, "Trying to manually refund swap");
|
|
|
|
let spend_key = if let Ok(published_refund_tx) =
|
|
state3.fetch_tx_refund(bitcoin_wallet.as_ref()).await
|
|
{
|
|
tracing::debug!(%swap_id, "Bitcoin refund transaction found, extracting key to refund Monero");
|
|
state3.extract_monero_private_key(published_refund_tx)?
|
|
} else {
|
|
let bob_peer_id = db.get_peer_id(swap_id)?;
|
|
return Ok(Err(Error::RefundTransactionNotPublishedYet(bob_peer_id)));
|
|
};
|
|
|
|
state3
|
|
.refund_xmr(
|
|
&monero_wallet,
|
|
monero_wallet_restore_blockheight,
|
|
swap_id.to_string(),
|
|
spend_key,
|
|
transfer_proof,
|
|
)
|
|
.await?;
|
|
|
|
let state = AliceState::XmrRefunded;
|
|
let db_state = (&state).into();
|
|
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
|
.await?;
|
|
|
|
Ok(Ok(state))
|
|
}
|