Skip to content

Commit

Permalink
Merge pull request #534 from TheCharlatan/handleMoneroFundingErrors
Browse files Browse the repository at this point in the history
Swap: Handle monero under- and overfunding
  • Loading branch information
zkao authored Jul 11, 2022
2 parents 4a1caf9 + 6252d13 commit fafad91
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 2 deletions.
43 changes: 41 additions & 2 deletions src/swapd/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,9 @@ impl Runtime {
self.send_wallet(msg_bus, endpoints, request)?;
}
// alice receives, bob sends
Msg::BuyProcedureSignature(buy_proc_sig) if self.state.a_refundsig() => {
Msg::BuyProcedureSignature(buy_proc_sig)
if self.state.a_refundsig() && !self.state.a_overfunded() =>
{
// Alice verifies that she has sent refund procedure signatures before
// processing the buy signatures from Bob
let tx_label = TxLabel::Buy;
Expand Down Expand Up @@ -1098,6 +1100,32 @@ impl Runtime {
id, hash, amount, block, tx
);
self.state.a_sup_refundsig_xmrlocked();

let required_funding_amount = self
.state
.a_required_funding_amount()
.expect("set when monero funding address is displayed");
if amount.clone() < required_funding_amount {
// Alice still views underfunding as valid in the hope that Bob still passes her BuyProcSig
let msg = format!(
"Too small amount funded. Required: {}, Funded: {}. Do not fund this swap anymore, will attempt to refund.",
monero::Amount::from_pico(required_funding_amount),
monero::Amount::from_pico(amount.clone())
);
error!("{}", msg);
self.report_progress_message_to(endpoints, self.enquirer.clone(), msg)?;
} else if amount.clone() > required_funding_amount {
// Alice set overfunded to ensure that she does not publish the buy transaction if Bob gives her the BuySig.
self.state.a_sup_overfunded();
let msg = format!(
"Too big amount funded. Required: {}, Funded: {}. Do not fund this swap anymore, will attempt to refund.",
monero::Amount::from_pico(required_funding_amount),
monero::Amount::from_pico(amount.clone())
);
error!("{}", msg);
self.report_progress_message_to(endpoints, self.enquirer.clone(), msg)?;
}

let txlabel = TxLabel::AccLock;
if !self.syncer_state.is_watched_tx(&txlabel) {
if self.syncer_state.awaiting_funding {
Expand Down Expand Up @@ -1135,7 +1163,9 @@ impl Runtime {
tx: _,
}) if self.state.swap_role() == SwapRole::Bob
&& self.syncer_state.tasks.watched_addrs.contains_key(id)
&& self.syncer_state.is_watched_addr(&TxLabel::AccLock) =>
&& self.syncer_state.is_watched_addr(&TxLabel::AccLock)
&& self.syncer_state.tasks.watched_addrs.get(id).unwrap()
== &TxLabel::AccLock =>
{
let amount = monero::Amount::from_pico(*amount);
if amount < self.syncer_state.monero_amount {
Expand Down Expand Up @@ -1519,6 +1549,7 @@ impl Runtime {
amount.bright_green_bold(),
address.addr(),
);
self.state.a_sup_required_funding_amount(amount);
let funding_request = Request::FundingInfo(
FundingInfo::Monero(MoneroFundingInfo {
swap_id,
Expand Down Expand Up @@ -1569,12 +1600,16 @@ impl Runtime {
&& self.state.a_refundsig()
&& !self.state.a_buy_published()
&& !self.state.cancel_seen()
&& !self.state.a_overfunded() // don't publish buy in case we overfunded
&& self.txs.contains_key(&TxLabel::Buy)
&& self.state.remote_params().is_some()
&& self.state.local_params().is_some() =>
{
let xmr_locked = self.state.a_xmr_locked();
let btc_locked = self.state.a_btc_locked();
let overfunded = self.state.a_overfunded();
let required_funding_amount =
self.state.a_required_funding_amount();
if let Some((txlabel, buy_tx)) =
self.txs.remove_entry(&TxLabel::Buy)
{
Expand All @@ -1591,6 +1626,8 @@ impl Runtime {
.state
.last_checkpoint_type()
.unwrap(),
required_funding_amount,
overfunded,
});
} else {
warn!(
Expand Down Expand Up @@ -2094,6 +2131,8 @@ impl Runtime {
cancel_seen: false,
refund_seen: false,
remote_params: self.state.remote_params().unwrap(),
required_funding_amount: None,
overfunded: false,
});
self.state_update(endpoints, next_state)?;
}
Expand Down
56 changes: 56 additions & 0 deletions src/swapd/swap_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub enum AliceState {
remote_params: Params,
/* #[display("local_view_share({0})")] */
local_params: Params,
required_funding_amount: Option<u64>, // TODO: Should be monero::Amount
overfunded: bool,
},
#[display("Finish({0})")]
FinishA(Outcome),
Expand Down Expand Up @@ -320,6 +322,24 @@ impl State {
pub fn a_refundsig(&self) -> bool {
matches!(self, State::Alice(AliceState::RefundSigA { .. }))
}
pub fn a_required_funding_amount(&self) -> Option<u64> {
match self {
State::Alice(AliceState::RefundSigA {
required_funding_amount,
..
}) => required_funding_amount.clone(),
_ => None,
}
}
pub fn a_overfunded(&self) -> bool {
matches!(
self,
State::Alice(AliceState::RefundSigA {
overfunded: true,
..
})
)
}
pub fn b_buy_tx_seen(&self) -> bool {
if !self.b_buy_sig() {
return false;
Expand Down Expand Up @@ -510,6 +530,22 @@ impl State {
false
}
}
/// Update Alice RefundSig state from overfunded=false to overfunded=true
pub fn a_sup_overfunded(&mut self) -> bool {
if let State::Alice(AliceState::RefundSigA { overfunded, .. }) = self {
if !*overfunded {
trace!("setting overfunded");
*overfunded = true;
true
} else {
warn!("overfunded was already set to true");
false
}
} else {
error!("Not on RefundSig state");
false
}
}
/// Update Alice RefundSig state from XMR unlocked to locked state
pub fn a_sup_refundsig_xmrlocked(&mut self) -> bool {
if let State::Alice(AliceState::RefundSigA { xmr_locked, .. }) = self {
Expand All @@ -526,6 +562,26 @@ impl State {
false
}
}
/// Update Alice RefundSig state with the required Monero funding amount
pub fn a_sup_required_funding_amount(&mut self, amount: monero::Amount) -> bool {
if let State::Alice(AliceState::RefundSigA {
required_funding_amount,
..
}) = self
{
if required_funding_amount.is_none() {
*required_funding_amount = Some(amount.as_pico());
true
} else {
warn!("required funding amount was already set");
false
}
} else {
error!("Not on RefundSig state");
false
}
}

pub fn a_sup_refundsig_refund_seen(&mut self) -> bool {
if let State::Alice(AliceState::RefundSigA { refund_seen, .. }) = self {
if !*refund_seen {
Expand Down
Loading

0 comments on commit fafad91

Please sign in to comment.