|
|
@ -1,9 +1,12 @@
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
use crate::network::swap_setup;
|
|
|
|
use std::fmt::Debug;
|
|
|
|
use crate::network::swap_setup::{
|
|
|
|
use std::task::{Context, Poll};
|
|
|
|
protocol, BlockchainNetwork, SpotPriceError, SpotPriceRequest, SpotPriceResponse,
|
|
|
|
use std::time::Duration;
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::protocol::alice::event_loop::LatestRate;
|
|
|
|
use anyhow::{anyhow, Context as _, Result};
|
|
|
|
use crate::protocol::alice::{State0, State3};
|
|
|
|
|
|
|
|
use crate::protocol::{alice, Message0, Message2, Message4};
|
|
|
|
|
|
|
|
use crate::{bitcoin, env, monero};
|
|
|
|
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
|
|
use futures::future::{BoxFuture, OptionFuture};
|
|
|
|
use futures::future::{BoxFuture, OptionFuture};
|
|
|
|
use futures::FutureExt;
|
|
|
|
use futures::FutureExt;
|
|
|
|
use libp2p::core::connection::ConnectionId;
|
|
|
|
use libp2p::core::connection::ConnectionId;
|
|
|
@ -13,19 +16,13 @@ use libp2p::swarm::{
|
|
|
|
ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol,
|
|
|
|
ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
use libp2p::{Multiaddr, PeerId};
|
|
|
|
use libp2p::{Multiaddr, PeerId};
|
|
|
|
use std::time::Instant;
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
|
|
|
use std::fmt::Debug;
|
|
|
|
|
|
|
|
use std::task::Poll;
|
|
|
|
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
use uuid::Uuid;
|
|
|
|
use uuid::Uuid;
|
|
|
|
use void::Void;
|
|
|
|
use void::Void;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::network::swap_setup;
|
|
|
|
|
|
|
|
use crate::network::swap_setup::{
|
|
|
|
|
|
|
|
protocol, BlockchainNetwork, SpotPriceError, SpotPriceRequest, SpotPriceResponse,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::protocol::alice::event_loop::LatestRate;
|
|
|
|
|
|
|
|
use crate::protocol::alice::{State0, State3};
|
|
|
|
|
|
|
|
use crate::protocol::{alice, Message0, Message2, Message4};
|
|
|
|
|
|
|
|
use crate::{bitcoin, env, monero};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
pub enum OutEvent {
|
|
|
|
pub enum OutEvent {
|
|
|
@ -39,7 +36,7 @@ pub enum OutEvent {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Error {
|
|
|
|
Error {
|
|
|
|
peer_id: PeerId,
|
|
|
|
peer_id: PeerId,
|
|
|
|
error: Error,
|
|
|
|
error: anyhow::Error,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -186,7 +183,7 @@ where
|
|
|
|
|
|
|
|
|
|
|
|
fn poll(
|
|
|
|
fn poll(
|
|
|
|
&mut self,
|
|
|
|
&mut self,
|
|
|
|
_cx: &mut Context<'_>,
|
|
|
|
_cx: &mut std::task::Context<'_>,
|
|
|
|
_params: &mut impl PollParameters,
|
|
|
|
_params: &mut impl PollParameters,
|
|
|
|
) -> Poll<NetworkBehaviourAction<(), Self::OutEvent>> {
|
|
|
|
) -> Poll<NetworkBehaviourAction<(), Self::OutEvent>> {
|
|
|
|
if let Some(event) = self.events.pop_front() {
|
|
|
|
if let Some(event) = self.events.pop_front() {
|
|
|
@ -197,7 +194,7 @@ where
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type InboundStream = BoxFuture<'static, anyhow::Result<(Uuid, alice::State3), Error>>;
|
|
|
|
type InboundStream = BoxFuture<'static, Result<(Uuid, alice::State3)>>;
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Handler<LR> {
|
|
|
|
pub struct Handler<LR> {
|
|
|
|
inbound_stream: OptionFuture<InboundStream>,
|
|
|
|
inbound_stream: OptionFuture<InboundStream>,
|
|
|
@ -239,7 +236,7 @@ impl<LR> Handler<LR> {
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
pub enum HandlerOutEvent {
|
|
|
|
pub enum HandlerOutEvent {
|
|
|
|
Initiated(bmrng::RequestReceiver<bitcoin::Amount, WalletSnapshot>),
|
|
|
|
Initiated(bmrng::RequestReceiver<bitcoin::Amount, WalletSnapshot>),
|
|
|
|
Completed(anyhow::Result<(Uuid, alice::State3), Error>),
|
|
|
|
Completed(Result<(Uuid, alice::State3)>),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<LR> ProtocolsHandler for Handler<LR>
|
|
|
|
impl<LR> ProtocolsHandler for Handler<LR>
|
|
|
@ -278,11 +275,12 @@ where
|
|
|
|
let protocol = tokio::time::timeout(self.timeout, async move {
|
|
|
|
let protocol = tokio::time::timeout(self.timeout, async move {
|
|
|
|
let request = swap_setup::read_cbor_message::<SpotPriceRequest>(&mut substream)
|
|
|
|
let request = swap_setup::read_cbor_message::<SpotPriceRequest>(&mut substream)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
.context("Failed to read spot price request")?;
|
|
|
|
|
|
|
|
|
|
|
|
let wallet_snapshot = sender
|
|
|
|
let wallet_snapshot = sender
|
|
|
|
.send_receive(request.btc)
|
|
|
|
.send_receive(request.btc)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.map_err(|e| Error::WalletSnapshotFailed(anyhow!(e)))?;
|
|
|
|
.context("Failed to receive wallet snapshot")?;
|
|
|
|
|
|
|
|
|
|
|
|
// wrap all of these into another future so we can `return` from all the
|
|
|
|
// wrap all of these into another future so we can `return` from all the
|
|
|
|
// different blocks
|
|
|
|
// different blocks
|
|
|
@ -334,24 +332,16 @@ where
|
|
|
|
Ok(xmr)
|
|
|
|
Ok(xmr)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let xmr = match validate.await {
|
|
|
|
let result = validate.await;
|
|
|
|
Ok(xmr) => {
|
|
|
|
|
|
|
|
swap_setup::write_cbor_message(&mut substream, SpotPriceResponse::Xmr(xmr))
|
|
|
|
|
|
|
|
.await
|
|
|
|
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xmr
|
|
|
|
swap_setup::write_cbor_message(
|
|
|
|
}
|
|
|
|
&mut substream,
|
|
|
|
Err(e) => {
|
|
|
|
SpotPriceResponse::from_result_ref(&result),
|
|
|
|
swap_setup::write_cbor_message(
|
|
|
|
)
|
|
|
|
&mut substream,
|
|
|
|
.await
|
|
|
|
SpotPriceResponse::Error(e.to_error_response()),
|
|
|
|
.context("Failed to write spot price response")?;
|
|
|
|
)
|
|
|
|
|
|
|
|
.await
|
|
|
|
let xmr = result?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
return Err(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let state0 = State0::new(
|
|
|
|
let state0 = State0::new(
|
|
|
|
request.btc,
|
|
|
|
request.btc,
|
|
|
@ -366,35 +356,32 @@ where
|
|
|
|
|
|
|
|
|
|
|
|
let message0 = swap_setup::read_cbor_message::<Message0>(&mut substream)
|
|
|
|
let message0 = swap_setup::read_cbor_message::<Message0>(&mut substream)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.context("Failed to deserialize message0")
|
|
|
|
.context("Failed to read message0")?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
let (swap_id, state1) = state0
|
|
|
|
let (swap_id, state1) = state0.receive(message0).map_err(Error::Io)?;
|
|
|
|
.receive(message0)
|
|
|
|
|
|
|
|
.context("Failed to transition state0 -> state1 using message0")?;
|
|
|
|
|
|
|
|
|
|
|
|
swap_setup::write_cbor_message(&mut substream, state1.next_message())
|
|
|
|
swap_setup::write_cbor_message(&mut substream, state1.next_message())
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
.context("Failed to send message1")?;
|
|
|
|
|
|
|
|
|
|
|
|
let message2 = swap_setup::read_cbor_message::<Message2>(&mut substream)
|
|
|
|
let message2 = swap_setup::read_cbor_message::<Message2>(&mut substream)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.context("Failed to deserialize message2")
|
|
|
|
.context("Failed to read message2")?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
let state2 = state1
|
|
|
|
let state2 = state1
|
|
|
|
.receive(message2)
|
|
|
|
.receive(message2)
|
|
|
|
.context("Failed to receive Message2")
|
|
|
|
.context("Failed to transition state1 -> state2 using message2")?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
swap_setup::write_cbor_message(&mut substream, state2.next_message())
|
|
|
|
swap_setup::write_cbor_message(&mut substream, state2.next_message())
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
.context("Failed to send message3")?;
|
|
|
|
|
|
|
|
|
|
|
|
let message4 = swap_setup::read_cbor_message::<Message4>(&mut substream)
|
|
|
|
let message4 = swap_setup::read_cbor_message::<Message4>(&mut substream)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.context("Failed to deserialize message4")
|
|
|
|
.context("Failed to read message4")?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
let state3 = state2
|
|
|
|
let state3 = state2
|
|
|
|
.receive(message4)
|
|
|
|
.receive(message4)
|
|
|
|
.context("Failed to receive Message4")
|
|
|
|
.context("Failed to transition state2 -> state3 using message4")?;
|
|
|
|
.map_err(Error::Io)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok((swap_id, state3))
|
|
|
|
Ok((swap_id, state3))
|
|
|
|
});
|
|
|
|
});
|
|
|
@ -402,8 +389,8 @@ where
|
|
|
|
let max_seconds = self.timeout.as_secs();
|
|
|
|
let max_seconds = self.timeout.as_secs();
|
|
|
|
self.inbound_stream = OptionFuture::from(Some(
|
|
|
|
self.inbound_stream = OptionFuture::from(Some(
|
|
|
|
async move {
|
|
|
|
async move {
|
|
|
|
protocol.await.map_err(|_| Error::Timeout {
|
|
|
|
protocol.await.with_context(|| {
|
|
|
|
seconds: max_seconds,
|
|
|
|
format!("Failed to complete execution setup within {}s", max_seconds)
|
|
|
|
})?
|
|
|
|
})?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.boxed(),
|
|
|
|
.boxed(),
|
|
|
@ -413,7 +400,7 @@ where
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn inject_fully_negotiated_outbound(&mut self, _: Void, _: Self::OutboundOpenInfo) {
|
|
|
|
fn inject_fully_negotiated_outbound(&mut self, _: Void, _: Self::OutboundOpenInfo) {
|
|
|
|
unreachable!("Alice does not support outbound in the hanlder")
|
|
|
|
unreachable!("Alice does not support outbound in the handler")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn inject_event(&mut self, _: Self::InEvent) {
|
|
|
|
fn inject_event(&mut self, _: Self::InEvent) {
|
|
|
@ -435,7 +422,7 @@ where
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
fn poll(
|
|
|
|
fn poll(
|
|
|
|
&mut self,
|
|
|
|
&mut self,
|
|
|
|
cx: &mut Context<'_>,
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
) -> Poll<
|
|
|
|
) -> Poll<
|
|
|
|
ProtocolsHandlerEvent<
|
|
|
|
ProtocolsHandlerEvent<
|
|
|
|
Self::OutboundProtocol,
|
|
|
|
Self::OutboundProtocol,
|
|
|
@ -460,8 +447,15 @@ where
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Differentiate between errors that we send back and shit that happens on
|
|
|
|
impl SpotPriceResponse {
|
|
|
|
// our side (IO, timeout)
|
|
|
|
pub fn from_result_ref(result: &Result<monero::Amount, Error>) -> Self {
|
|
|
|
|
|
|
|
match result {
|
|
|
|
|
|
|
|
Ok(amount) => SpotPriceResponse::Xmr(*amount),
|
|
|
|
|
|
|
|
Err(error) => SpotPriceResponse::Error(error.to_error_response()),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum Error {
|
|
|
|
pub enum Error {
|
|
|
|
#[error("ASB is running in resume-only mode")]
|
|
|
|
#[error("ASB is running in resume-only mode")]
|
|
|
@ -490,12 +484,6 @@ pub enum Error {
|
|
|
|
cli: BlockchainNetwork,
|
|
|
|
cli: BlockchainNetwork,
|
|
|
|
asb: BlockchainNetwork,
|
|
|
|
asb: BlockchainNetwork,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
#[error("Io Error")]
|
|
|
|
|
|
|
|
Io(#[source] anyhow::Error),
|
|
|
|
|
|
|
|
#[error("Failed to request wallet snapshot")]
|
|
|
|
|
|
|
|
WalletSnapshotFailed(#[source] anyhow::Error),
|
|
|
|
|
|
|
|
#[error("Failed to complete execution setup within {seconds}s")]
|
|
|
|
|
|
|
|
Timeout { seconds: u64 },
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Error {
|
|
|
|
impl Error {
|
|
|
@ -517,11 +505,9 @@ impl Error {
|
|
|
|
asb: *asb,
|
|
|
|
asb: *asb,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Error::LatestRateFetchFailed(_)
|
|
|
|
Error::LatestRateFetchFailed(_) | Error::SellQuoteCalculationFailed(_) => {
|
|
|
|
| Error::SellQuoteCalculationFailed(_)
|
|
|
|
SpotPriceError::Other
|
|
|
|
| Error::WalletSnapshotFailed(_)
|
|
|
|
}
|
|
|
|
| Error::Timeout { .. }
|
|
|
|
|
|
|
|
| Error::Io(_) => SpotPriceError::Other,
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|