This commit is contained in:
Thomas Eizinger 2021-06-24 15:43:01 +10:00
parent 144145fc06
commit a3e6f75eb1
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
7 changed files with 339 additions and 304 deletions

View File

@ -81,6 +81,9 @@ pub struct PublicViewKey(PublicKey);
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd)]
pub struct Amount(u64);
// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, XMR 0.000_015 * 2 (to be on the safe side)
pub const MONERO_FEE: Amount = Amount::from_piconero(30000000);
impl Amount {
pub const ZERO: Self = Self(0);
pub const ONE_XMR: Self = Self(PICONERO_OFFSET);
@ -88,7 +91,7 @@ impl Amount {
/// piconeros.
///
/// A piconero (a.k.a atomic unit) is equal to 1e-12 XMR.
pub fn from_piconero(amount: u64) -> Self {
pub const fn from_piconero(amount: u64) -> Self {
Amount(amount)
}

View File

@ -276,11 +276,6 @@ impl Wallet {
pub async fn refresh(&self) -> Result<Refreshed> {
Ok(self.inner.lock().await.refresh().await?)
}
pub fn static_tx_fee_estimate(&self) -> Amount {
// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, 0.000_015 * 2 (to be on the safe side)
Amount::from_monero(0.000_03f64).expect("static fee to be convertible without problems")
}
}
#[derive(Debug)]

View File

@ -1,19 +1,21 @@
//! Run an XMR/BTC swap in the role of Alice.
//! Alice holds XMR and wishes receive BTC.
use std::sync::Arc;
use uuid::Uuid;
use crate::{bitcoin, monero};
use crate::database::Database;
use crate::env::Config;
use crate::{bitcoin, monero};
use std::sync::Arc;
use uuid::Uuid;
pub use self::behaviour::{Behaviour, OutEvent};
pub use self::event_loop::{EventLoop, EventLoopHandle};
pub use self::recovery::{cancel, punish, redeem, refund, safely_abort};
pub use self::recovery::cancel::cancel;
pub use self::recovery::punish::punish;
pub use self::recovery::redeem::redeem;
pub use self::recovery::refund::refund;
pub use self::recovery::safely_abort::safely_abort;
pub use self::recovery::{cancel, punish, redeem, refund, safely_abort};
pub use self::state::*;
pub use self::swap::{run, run_until};
@ -21,9 +23,9 @@ mod behaviour;
pub mod event_loop;
mod execution_setup;
mod recovery;
mod swap_setup;
pub mod state;
pub mod swap;
pub mod swap_setup;
pub struct Swap {
pub state: AliceState,

View File

@ -1,24 +1,25 @@
use crate::network::quote::BidQuote;
use crate::network::{encrypted_signature, quote, transfer_proof};
use crate::protocol::alice::event_loop::LatestRate;
use crate::protocol::alice::{execution_setup, swap_setup, State3};
use crate::{env, monero};
use anyhow::{anyhow, Error};
use libp2p::{NetworkBehaviour, PeerId};
use libp2p::ping::{Ping, PingEvent};
use libp2p::request_response::{RequestId, ResponseChannel};
use libp2p::{NetworkBehaviour, PeerId};
use uuid::Uuid;
use crate::{env, monero};
use crate::network::{encrypted_signature, quote, transfer_proof};
use crate::network::quote::BidQuote;
use crate::protocol::alice::{execution_setup, State3, swap_setup};
use crate::protocol::alice::event_loop::LatestRate;
use crate::protocol::alice::swap_setup::WalletSnapshot;
use tokio::sync::oneshot;
#[derive(Debug)]
pub enum OutEvent {
SwapRequestDeclined {
peer: PeerId,
error: swap_setup::Error,
},
ExecutionSetupStart {
peer: PeerId,
btc: bitcoin::Amount,
xmr: monero::Amount,
SwapInitiated {
send_wallet_snapshot: oneshot::Sender<WalletSnapshot>
},
QuoteRequested {
channel: ResponseChannel<BidQuote>,

View File

@ -20,6 +20,7 @@ use std::fmt::Debug;
use std::sync::Arc;
use tokio::sync::mpsc;
use uuid::Uuid;
use crate::protocol::alice::swap_setup::WalletSnapshot;
/// A future that resolves to a tuple of `PeerId`, `transfer_proof::Request` and
/// `Responder`.
@ -150,61 +151,38 @@ where
tokio::select! {
swarm_event = self.swarm.next_event() => {
match swarm_event {
SwarmEvent::Behaviour(OutEvent::ExecutionSetupStart { peer, btc, xmr }) => {
SwarmEvent::Behaviour(OutEvent::SwapInitiated { send_wallet_snapshot }) => {
let tx_redeem_fee = self.bitcoin_wallet
.estimate_fee(bitcoin::TxRedeem::weight(), btc)
.await;
let tx_punish_fee = self.bitcoin_wallet
.estimate_fee(bitcoin::TxPunish::weight(), btc)
.await;
let redeem_address = self.bitcoin_wallet.new_address().await;
let punish_address = self.bitcoin_wallet.new_address().await;
let (redeem_address, punish_address) = match (
redeem_address,
punish_address,
) {
(Ok(redeem_address), Ok(punish_address)) => {
(redeem_address, punish_address)
}
_ => {
tracing::error!(%peer, "Failed to get new address during execution setup");
continue;
}
};
let (tx_redeem_fee, tx_punish_fee) = match (
tx_redeem_fee,
tx_punish_fee,
) {
(Ok(tx_redeem_fee), Ok(tx_punish_fee)) => {
(tx_redeem_fee, tx_punish_fee)
}
_ => {
tracing::error!(%peer, "Failed to calculate transaction fees during execution setup");
continue;
}
};
let state0 = match State0::new(
btc,
xmr,
self.env_config,
redeem_address,
punish_address,
tx_redeem_fee,
tx_punish_fee,
&mut OsRng
) {
Ok(state) => state,
let wallet_snapshot = match WalletSnapshot::capture(&self.bitcoin_wallet, &self.monero_wallet).await {
Ok(wallet_snapshot) => wallet_snapshot,
Err(error) => {
tracing::warn!(%peer, "Failed to make State0 for execution setup. Error {:#}", error);
continue;
tracing::error!("Swap request will be ignored because we were unable to create wallet snapshot for swap: {:#}", error)
}
};
self.swarm.behaviour_mut().execution_setup.run(peer, state0);
match send_wallet_snapshot.send().await {
Ok()
}
// TODO: Move into execution setup as part of swap_setup
// let state0 = match State0::new(
// btc,
// xmr,
// self.env_config,
// redeem_address,
// punish_address,
// tx_redeem_fee,
// tx_punish_fee,
// &mut OsRng
// ) {
// Ok(state) => state,
// Err(error) => {
// tracing::warn!(%peer, "Failed to make State0 for execution setup. Error {:#}", error);
// continue;
// }
// };
//
// self.swarm.behaviour_mut().execution_setup.run(peer, state0);
}
SwarmEvent::Behaviour(OutEvent::SwapRequestDeclined { peer, error }) => {
tracing::warn!(%peer, "Ignoring spot price request because: {}", error);

View File

@ -52,36 +52,7 @@ impl Behaviour {
pub fn run(&mut self, bob: PeerId, state0: State0) {
self.inner.do_protocol_listener(bob, move |mut substream| {
let protocol = async move {
let message0 =
serde_cbor::from_slice::<Message0>(&substream.read_message(BUF_SIZE).await?)
.context("Failed to deserialize message0")?;
let (swap_id, state1) = state0.receive(message0)?;
substream
.write_message(
&serde_cbor::to_vec(&state1.next_message())
.context("Failed to serialize message1")?,
)
.await?;
let message2 =
serde_cbor::from_slice::<Message2>(&substream.read_message(BUF_SIZE).await?)
.context("Failed to deserialize message2")?;
let state2 = state1
.receive(message2)
.context("Failed to receive Message2")?;
substream
.write_message(
&serde_cbor::to_vec(&state2.next_message())
.context("Failed to serialize message3")?,
)
.await?;
let message4 =
serde_cbor::from_slice::<Message4>(&substream.read_message(BUF_SIZE).await?)
.context("Failed to deserialize message4")?;
let state3 = state2.receive(message4)?;
Ok((bob, (swap_id, state3)))
};

View File

@ -1,33 +1,75 @@
use crate::network::cbor_request_response::CborCodec;
use crate::network::spot_price;
use crate::network::spot_price::{BlockchainNetwork, SpotPriceProtocol};
use crate::protocol::alice;
use crate::protocol::alice::event_loop::LatestRate;
use crate::{env, monero};
use anyhow::{Result, Context};
use std::collections::VecDeque;
use std::fmt::Debug;
use std::future;
use std::task::{Context, Poll};
use futures::future::{BoxFuture, OptionFuture};
use libp2p::{Multiaddr, NetworkBehaviour, PeerId};
use libp2p::core::connection::ConnectionId;
use libp2p::core::{Endpoint, upgrade};
use libp2p::core::upgrade::from_fn;
use libp2p::core::upgrade::FromFnUpgrade;
use libp2p::request_response::{
ProtocolSupport, RequestResponseConfig, RequestResponseEvent, RequestResponseMessage,
ResponseChannel,
};
use libp2p::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters, NetworkBehaviour, IntoProtocolsHandler, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, KeepAlive, SubstreamProtocol, NegotiatedSubstream};
use libp2p::{NetworkBehaviour, PeerId, Multiaddr};
use std::collections::VecDeque;
use std::fmt::Debug;
use std::task::{Context, Poll};
use libp2p::core::connection::ConnectionId;
use libp2p::swarm::protocols_handler::{OutboundUpgradeSend, InboundUpgradeSend};
use libp2p::core::
use libp2p::swarm::{IntoProtocolsHandler, KeepAlive, NegotiatedSubstream, NetworkBehaviour, NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol};
use libp2p::swarm::protocols_handler::{InboundUpgradeSend, OutboundUpgradeSend};
use uuid::Uuid;
use void::Void;
use crate::{env, monero};
use crate::network::cbor_request_response::CborCodec;
use crate::network::spot_price;
use crate::network::spot_price::{BlockchainNetwork, SpotPriceProtocol};
use crate::protocol::{alice, bob, Message0, Message2, Message4};
use crate::protocol::alice::event_loop::LatestRate;
use crate::protocol::alice::{State3, State0};
use futures::FutureExt;
use tokio::sync::oneshot;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub enum OutEvent {
ExecutionSetupParams {
peer: PeerId,
btc: bitcoin::Amount,
xmr: monero::Amount,
Initiated {
send_wallet_snapshot: oneshot::Sender<WalletSnapshot>
},
Error {
peer: PeerId,
error: Error,
Completed {
bob_peer_id: PeerId,
swap_id: Uuid,
state3: State3,
},
Error, // TODO be more descriptive
}
pub struct WalletSnapshot {
balance: monero::Amount,
lock_fee: monero::Amount,
// TODO: Consider using the same address for punish and redeem (they are mutually exclusive, so effectively the address will only be used once)
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
redeem_fee: bitcoin::Amount,
refund_fee: bitcoin::Amount,
}
impl WalletSnapshot {
pub async fn capture(bitcoin_wallet: &bitcoin::Wallet, monero_wallet: &monero::Wallet) -> Result<Self> {
Ok(Self {
balance: monero_wallet.get_balance().await?,
lock_fee: monero::MONERO_FEE,
redeem_address: bitcoin_wallet.new_address().await?,
punish_address: bitcoin_wallet.new_address().await?,
redeem_fee: bitcoin_wallet
.estimate_fee(bitcoin::TxRedeem::weight(), btc)
.await,
refund_fee: bitcoin_wallet
.estimate_fee(bitcoin::TxPunish::weight(), btc)
.await
})
}
}
#[allow(missing_debug_implementations)]
@ -36,24 +78,14 @@ where
LR: LatestRate + Send + 'static,
{
events: VecDeque<OutEvent>,
balance: monero::Amount,
lock_fee: monero::Amount,
min_buy: bitcoin::Amount,
max_buy: bitcoin::Amount,
env_config: env::Config,
unused_addresses: VecDeque<bitcoin::Address>,
redeem_fee: bitcoin::Amount,
refund_fee: bitcoin::Amount,
latest_rate: LR,
resume_only: bool,
}
/// Behaviour that handles spot prices.
/// All the logic how to react to a spot price request is contained here, events
/// reporting the successful handling of a spot price request or a failure are
/// bubbled up to the parent behaviour.
impl<LR> Behaviour<LR>
where
LR: LatestRate + Send + 'static,
@ -79,37 +111,17 @@ where
}
}
pub fn update_balance(&mut self, balance: monero::Amount) {
self.balance = balance;
}
fn decline(
&mut self,
peer: PeerId,
channel: ResponseChannel<spot_price::Response>,
error: Error,
) {
if self
.behaviour
.send_response(
channel,
spot_price::Response::Error(error.to_error_response()),
)
.is_err()
{
tracing::debug!(%peer, "Unable to send error response for spot price request");
}
self.events.push_back(OutEvent::Error { peer, error });
pub fn update(&mut self, monero_balance: monero::Amount, redeem_address: bitcoin::Address, punish_address: bitcoin::Address) {
self.balance = monero_balance;
}
}
impl<LR> NetworkBehaviour for Behaviour<LR> {
type ProtocolsHandler = ();
type OutEvent = ();
type ProtocolsHandler = Handler;
type OutEvent = OutEvent;
fn new_handler(&mut self) -> Self::ProtocolsHandler {
todo!()
Handler::default()
}
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
@ -133,33 +145,189 @@ impl<LR> NetworkBehaviour for Behaviour<LR> {
}
}
enum InboundState {
PendingBehaviour(NegotiatedSubstream)
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SpotPriceRequest {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
pub btc: bitcoin::Amount,
pub blockchain_network: BlockchainNetwork,
}
struct Handler {
inbound_stream: Option<InboundState>
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SpotPriceError {
NoSwapsAccepted,
AmountBelowMinimum {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
min: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
buy: bitcoin::Amount,
},
AmountAboveMaximum {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
max: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
buy: bitcoin::Amount,
},
BalanceTooLow {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
buy: bitcoin::Amount,
},
BlockchainNetworkMismatch {
cli: BlockchainNetwork,
asb: BlockchainNetwork,
},
/// To be used for errors that cannot be explained on the CLI side (e.g.
/// rate update problems on the seller side)
Other,
}
// TODO: This is bob only.
// enum OutboundState {
// PendingOpen(
// // TODO: put data in here we pass in when we want to kick of swap setup, just bitcoin amount?
// ),
// PendingNegotiate,
// Executing(BoxFuture<'static, anyhow::Result<(Uuid, bob::State3)>>)
// }
// TODO: Don't just use anyhow::Error
type InboundStream = BoxFuture<'static, anyhow::Result<(Uuid, alice::State3)>>;
struct Handler {
inbound_stream: OptionFuture<InboundStream>,
events: VecDeque<HandlerOutEvent>,
resume_only: bool
}
enum HandlerOutEvent {
Initiated(oneshot::Sender<WalletSnapshot>),
Completed(anyhow::Result<(Uuid, alice::State3)>)
}
pub const BUF_SIZE: usize = 1024 * 1024;
impl ProtocolsHandler for Handler {
type InEvent = ();
type OutEvent = ();
type OutEvent = HandlerOutEvent;
type Error = ();
type InboundProtocol = ();
type InboundProtocol = protocol::SwapSetup;
type OutboundProtocol = ();
type InboundOpenInfo = ();
type OutboundOpenInfo = ();
fn listen_protocol(&self) -> SubstreamProtocol<Self::InboundProtocol, Self::InboundOpenInfo> {
todo!()
SubstreamProtocol::new(protocol::new(), todo!("pass data down to handler"))
}
fn inject_fully_negotiated_inbound(&mut self, protocol: NegotiatedSubstream, _: Self::InboundOpenInfo) {
self.inbound_stream = Some(InboundState::PendingBehaviour(protocol));
fn inject_fully_negotiated_inbound(&mut self, mut protocol: NegotiatedSubstream, _: Self::InboundOpenInfo) {
let (sender, receiver) = oneshot::channel();
let resume_only = self.resume_only;
self.inbound_stream = OptionFuture::from(Some(async move {
let request = read_cbor_message::<SpotPriceRequest>(&mut protocol).await?;
let wallet_snapshot = receiver.await?; // TODO Put a timeout on this
async {
if resume_only {
return Err(Error::ResumeOnlyMode)
};
}
let blockchain_network = BlockchainNetwork {
bitcoin: self.env_config.bitcoin_network,
monero: self.env_config.monero_network,
};
if request.blockchain_network != blockchain_network {
self.decline(peer, channel, Error::BlockchainNetworkMismatch {
cli: request.blockchain_network,
asb: blockchain_network,
});
return;
}
let btc = request.btc;
if btc < self.min_buy {
self.decline(peer, channel, Error::AmountBelowMinimum {
min: self.min_buy,
buy: btc,
});
return;
}
if btc > self.max_buy {
self.decline(peer, channel, Error::AmountAboveMaximum {
max: self.max_buy,
buy: btc,
});
return;
}
let rate = match self.latest_rate.latest_rate() {
Ok(rate) => rate,
Err(e) => {
self.decline(peer, channel, Error::LatestRateFetchFailed(Box::new(e)));
return;
}
};
let xmr = match rate.sell_quote(btc) {
Ok(xmr) => xmr,
Err(e) => {
self.decline(peer, channel, Error::SellQuoteCalculationFailed(e));
return;
}
};
let xmr_balance = self.balance;
let xmr_lock_fees = self.lock_fee;
if xmr_balance < xmr + xmr_lock_fees {
self.decline(peer, channel, Error::BalanceTooLow {
balance: xmr_balance,
buy: btc,
});
return;
}
if self
.behaviour
.send_response(channel, spot_price::Response::Xmr(xmr))
.is_err()
{
tracing::error!(%peer, "Failed to send spot price response of {} for {}", xmr, btc)
}
let state0 = State0::new(spot_price_request.btc, todo!(), todo!(), todo!(), todo!(), todo!(), todo!(), todo!())?;
let message0 = read_cbor_message::<Message0>(&mut protocol).context("Failed to deserialize message0")?;
let (swap_id, state1) = state0.receive(message0)?;
write_cbor_message(&mut protocol, state1.next_message()).await?;
let message2 = read_cbor_message::<Message2>(&mut protocol).context("Failed to deserialize message2")?;
let state2 = state1
.receive(message2)
.context("Failed to receive Message2")?;
write_cbor_message(&mut protocol, state2.next_message()).await?;
let message4 = read_cbor_message::<Message4>(&mut protocol).context("Failed to deserialize message4")?;
let state3 = state2
.receive(message4)
.context("Failed to receive Message4")?;
Ok((swap_id, state3))
}.boxed()));
self.events.push_back(HandlerOutEvent::Initiated(sender));
}
fn inject_fully_negotiated_outbound(&mut self, protocol: _, info: Self::OutboundOpenInfo) {
todo!()
fn inject_fully_negotiated_outbound(&mut self, protocol: NegotiatedSubstream, info: Self::OutboundOpenInfo) {
unreachable!("we don't support outbound")
}
fn inject_event(&mut self, event: Self::InEvent) {
@ -175,145 +343,60 @@ impl ProtocolsHandler for Handler {
}
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<ProtocolsHandlerEvent<Self::OutboundProtocol, Self::OutboundOpenInfo, Self::OutEvent, Self::Error>> {
todo!()
let event = futures::ready!(self.inbound_stream.poll(cx));
Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerOutEvent::Completed(event)))
}
}
pub fn new() -> SwapSetup {
from_fn(
b"/rendezvous/1.0.0",
Box::new(|socket, _| future::ready(Ok(socket))),
)
async fn read_cbor_message<T>(substream: &mut NegotiatedSubstream) -> Result<T> where T: Deserialize {
let bytes = upgrade::read_one(substream, BUF_SIZE).await?;
let mut de = serde_cbor::Deserializer::from_slice(&bytes);
let message = T::deserialize(de)?;
Ok(message)
}
pub type SwapSetup = FromFnUpgrade<
&'static [u8],
Box<
dyn Fn(
NegotiatedSubstream,
Endpoint,
async fn write_cbor_message<T>(substream: &mut NegotiatedSubstream, message: T) -> Result<()> where T: Serialize {
let bytes = serde_cbor::to_vec(&message)?;
upgrade::write_one(substream, &bytes).await?;
Ok(())
}
async fn write_error_message(substream: &mut NegotiatedSubstream, message: impl Into<SpotPriceError>) -> Result<()> {
let bytes = serde_cbor::to_vec(&message.into())?;
upgrade::write_one(substream, &bytes).await?;
Ok(())
}
mod protocol {
use super::*;
pub fn new() -> SwapSetup {
from_fn(
b"/comit/xmr/btc/swap_setup/1.0.0",
Box::new(|socket, endpoint| future::ready(match endpoint {
Endpoint::Listener => Ok(socket),
Endpoint::Dialer => todo!("return error")
})),
)
-> future::Ready<Result<NegotiatedSubstream, Void>>
+ Send
+ 'static,
>,
>;
impl<LR> NetworkBehaviourEventProcess<spot_price::OutEvent> for Behaviour<LR>
where
LR: LatestRate + Send + 'static,
{
fn inject_event(&mut self, event: spot_price::OutEvent) {
let (peer, message) = match event {
RequestResponseEvent::Message { peer, message } => (peer, message),
RequestResponseEvent::OutboundFailure { peer, error, .. } => {
tracing::error!(%peer, "Failure sending spot price response: {:#}", error);
return;
}
RequestResponseEvent::InboundFailure { peer, error, .. } => {
tracing::warn!(%peer, "Inbound failure when handling spot price request: {:#}", error);
return;
}
RequestResponseEvent::ResponseSent { peer, .. } => {
tracing::debug!(%peer, "Spot price response sent");
return;
}
};
let (request, channel) = match message {
RequestResponseMessage::Request {
request, channel, ..
} => (request, channel),
RequestResponseMessage::Response { .. } => {
tracing::error!("Unexpected message");
return;
}
};
let blockchain_network = BlockchainNetwork {
bitcoin: self.env_config.bitcoin_network,
monero: self.env_config.monero_network,
};
if request.blockchain_network != blockchain_network {
self.decline(peer, channel, Error::BlockchainNetworkMismatch {
cli: request.blockchain_network,
asb: blockchain_network,
});
return;
}
if self.resume_only {
self.decline(peer, channel, Error::ResumeOnlyMode);
return;
}
let btc = request.btc;
if btc < self.min_buy {
self.decline(peer, channel, Error::AmountBelowMinimum {
min: self.min_buy,
buy: btc,
});
return;
}
if btc > self.max_buy {
self.decline(peer, channel, Error::AmountAboveMaximum {
max: self.max_buy,
buy: btc,
});
return;
}
let rate = match self.latest_rate.latest_rate() {
Ok(rate) => rate,
Err(e) => {
self.decline(peer, channel, Error::LatestRateFetchFailed(Box::new(e)));
return;
}
};
let xmr = match rate.sell_quote(btc) {
Ok(xmr) => xmr,
Err(e) => {
self.decline(peer, channel, Error::SellQuoteCalculationFailed(e));
return;
}
};
let xmr_balance = self.balance;
let xmr_lock_fees = self.lock_fee;
if xmr_balance < xmr + xmr_lock_fees {
self.decline(peer, channel, Error::BalanceTooLow {
balance: xmr_balance,
buy: btc,
});
return;
}
if self
.behaviour
.send_response(channel, spot_price::Response::Xmr(xmr))
.is_err()
{
tracing::error!(%peer, "Failed to send spot price response of {} for {}", xmr, btc)
}
self.events
.push_back(OutEvent::ExecutionSetupParams { peer, btc, xmr });
}
}
impl From<OutEvent> for alice::OutEvent {
fn from(event: OutEvent) -> Self {
match event {
OutEvent::ExecutionSetupParams { peer, btc, xmr } => {
Self::ExecutionSetupStart { peer, btc, xmr }
}
OutEvent::Error { peer, error } => Self::SwapRequestDeclined { peer, error },
}
}
pub type SwapSetup = FromFnUpgrade<
&'static [u8],
Box<
dyn Fn(
NegotiatedSubstream,
Endpoint,
)
-> future::Ready<Result<NegotiatedSubstream, Void>>
+ Send
+ 'static,
>,
>;
}
#[derive(Debug, thiserror::Error)]
@ -374,16 +457,18 @@ impl Error {
#[cfg(test)]
mod tests {
use super::*;
use crate::asb::Rate;
use crate::env::GetConfig;
use crate::monero;
use crate::network::test::{await_events_or_timeout, connect, new_swarm};
use crate::protocol::{alice, bob};
use anyhow::anyhow;
use libp2p::Swarm;
use rust_decimal::Decimal;
use crate::{monero, network};
use crate::asb::Rate;
use crate::env::GetConfig;
use crate::network::test::{await_events_or_timeout, connect, new_swarm};
use crate::protocol::{alice, bob};
use super::*;
impl Default for AliceBehaviourValues {
fn default() -> Self {
Self {