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.
292 lines
9.1 KiB
292 lines
9.1 KiB
use crate::address_map::{ActorName, Stopping};
|
|
use crate::model::cfd::{Cfd, CfdState, Dlc, Order, OrderId, Role};
|
|
use crate::model::{Identity, Usd};
|
|
use crate::oracle::Announcement;
|
|
use crate::setup_contract::{self, SetupParams};
|
|
use crate::tokio_ext::spawn_fallible;
|
|
use crate::wire::{self, SetupMsg};
|
|
use crate::{maker_inc_connections, wallet};
|
|
use anyhow::{Context, Result};
|
|
use async_trait::async_trait;
|
|
use futures::channel::mpsc::{self, UnboundedSender};
|
|
use futures::{future, SinkExt};
|
|
use maia::secp256k1_zkp::schnorrsig;
|
|
use xtra::prelude::MessageChannel;
|
|
use xtra_productivity::xtra_productivity;
|
|
|
|
pub struct Actor {
|
|
order: Order,
|
|
quantity: Usd,
|
|
n_payouts: usize,
|
|
oracle_pk: schnorrsig::PublicKey,
|
|
announcement: Announcement,
|
|
build_party_params: Box<dyn MessageChannel<wallet::BuildPartyParams>>,
|
|
sign: Box<dyn MessageChannel<wallet::Sign>>,
|
|
taker: Box<dyn MessageChannel<maker_inc_connections::TakerMessage>>,
|
|
confirm_order: Box<dyn MessageChannel<maker_inc_connections::ConfirmOrder>>,
|
|
taker_id: Identity,
|
|
on_completed: Box<dyn MessageChannel<Completed>>,
|
|
on_stopping: Vec<Box<dyn MessageChannel<Stopping<Self>>>>,
|
|
setup_msg_sender: Option<UnboundedSender<SetupMsg>>,
|
|
}
|
|
|
|
impl Actor {
|
|
pub fn new(
|
|
(order, quantity, n_payouts): (Order, Usd, usize),
|
|
(oracle_pk, announcement): (schnorrsig::PublicKey, Announcement),
|
|
build_party_params: &(impl MessageChannel<wallet::BuildPartyParams> + 'static),
|
|
sign: &(impl MessageChannel<wallet::Sign> + 'static),
|
|
(taker, confirm_order, taker_id): (
|
|
&(impl MessageChannel<maker_inc_connections::TakerMessage> + 'static),
|
|
&(impl MessageChannel<maker_inc_connections::ConfirmOrder> + 'static),
|
|
Identity,
|
|
),
|
|
on_completed: &(impl MessageChannel<Completed> + 'static),
|
|
(on_stopping0, on_stopping1): (
|
|
&(impl MessageChannel<Stopping<Self>> + 'static),
|
|
&(impl MessageChannel<Stopping<Self>> + 'static),
|
|
),
|
|
) -> Self {
|
|
Self {
|
|
order,
|
|
quantity,
|
|
n_payouts,
|
|
oracle_pk,
|
|
announcement,
|
|
build_party_params: build_party_params.clone_channel(),
|
|
sign: sign.clone_channel(),
|
|
taker: taker.clone_channel(),
|
|
confirm_order: confirm_order.clone_channel(),
|
|
taker_id,
|
|
on_completed: on_completed.clone_channel(),
|
|
on_stopping: vec![on_stopping0.clone_channel(), on_stopping1.clone_channel()],
|
|
setup_msg_sender: None,
|
|
}
|
|
}
|
|
|
|
async fn contract_setup(&mut self, this: xtra::Address<Self>) -> Result<()> {
|
|
let order_id = self.order.id;
|
|
let cfd = Cfd::new(
|
|
self.order.clone(),
|
|
self.quantity,
|
|
CfdState::contract_setup(),
|
|
self.taker_id,
|
|
);
|
|
|
|
let (sender, receiver) = mpsc::unbounded();
|
|
// store the writing end to forward messages from the taker to
|
|
// the spawned contract setup task
|
|
self.setup_msg_sender = Some(sender);
|
|
|
|
let taker_id = self.taker_id;
|
|
let contract_future = setup_contract::new(
|
|
self.taker.sink().with(move |msg| {
|
|
future::ok(maker_inc_connections::TakerMessage {
|
|
taker_id,
|
|
msg: wire::MakerToTaker::Protocol { order_id, msg },
|
|
})
|
|
}),
|
|
receiver,
|
|
(self.oracle_pk, self.announcement.clone()),
|
|
SetupParams::new(
|
|
cfd.margin()?,
|
|
cfd.counterparty_margin()?,
|
|
cfd.order.price,
|
|
cfd.quantity_usd,
|
|
cfd.order.leverage,
|
|
cfd.refund_timelock_in_blocks(),
|
|
cfd.order.fee_rate,
|
|
),
|
|
self.build_party_params.clone_channel(),
|
|
self.sign.clone_channel(),
|
|
Role::Maker,
|
|
self.n_payouts,
|
|
);
|
|
|
|
spawn_fallible::<_, anyhow::Error>(async move {
|
|
let _ = match contract_future.await {
|
|
Ok(dlc) => this.send(SetupSucceeded { order_id, dlc }).await?,
|
|
Err(error) => this.send(SetupFailed { order_id, error }).await?,
|
|
};
|
|
|
|
Ok(())
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn complete(&mut self, completed: Completed, ctx: &mut xtra::Context<Self>) {
|
|
let _ = self.on_completed.send(completed).await;
|
|
|
|
ctx.stop();
|
|
}
|
|
}
|
|
|
|
#[xtra_productivity]
|
|
impl Actor {
|
|
fn handle(&mut self, _msg: Accepted, ctx: &mut xtra::Context<Self>) {
|
|
let order_id = self.order.id;
|
|
tracing::info!(%order_id, "Maker accepts an order");
|
|
|
|
let this = ctx
|
|
.address()
|
|
.expect("actor to be able to give address to itself");
|
|
|
|
let fut = async {
|
|
self.confirm_order
|
|
.send(maker_inc_connections::ConfirmOrder {
|
|
taker_id: self.taker_id,
|
|
order_id,
|
|
address: this.clone(),
|
|
})
|
|
.await
|
|
.context("Failed to deliver order confirmation")??;
|
|
|
|
self.contract_setup(this)
|
|
.await
|
|
.context("Failed to start contract setup")?;
|
|
|
|
Ok(())
|
|
};
|
|
|
|
if let Err(error) = fut.await {
|
|
tracing::warn!(%order_id, "Stopping setup_maker actor: {}", error);
|
|
|
|
self.complete(Completed::Failed { order_id, error }, ctx)
|
|
.await;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
fn handle(&mut self, _msg: Rejected, ctx: &mut xtra::Context<Self>) {
|
|
self.complete(Completed::Rejected(self.order.id), ctx).await;
|
|
}
|
|
|
|
fn handle(&mut self, msg: SetupSucceeded, ctx: &mut xtra::Context<Self>) {
|
|
self.complete(
|
|
Completed::NewContract {
|
|
order_id: msg.order_id,
|
|
dlc: msg.dlc,
|
|
},
|
|
ctx,
|
|
)
|
|
.await
|
|
}
|
|
|
|
fn handle(&mut self, msg: SetupFailed, ctx: &mut xtra::Context<Self>) {
|
|
self.complete(
|
|
Completed::Failed {
|
|
order_id: msg.order_id,
|
|
error: msg.error,
|
|
},
|
|
ctx,
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
#[xtra_productivity(message_impl = false)]
|
|
impl Actor {
|
|
fn handle(&mut self, msg: wire::SetupMsg, _ctx: &mut xtra::Context<Self>) -> Result<()> {
|
|
let mut sender = self
|
|
.setup_msg_sender
|
|
.clone()
|
|
.context("Cannot forward message to contract setup task")?;
|
|
sender.send(msg).await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl xtra::Actor for Actor {
|
|
async fn started(&mut self, ctx: &mut xtra::Context<Self>) {
|
|
let quantity = self.quantity;
|
|
let order = self.order.clone();
|
|
if quantity < order.min_quantity || quantity > order.max_quantity {
|
|
tracing::info!(
|
|
"Order rejected: quantity {} not in range [{}, {}]",
|
|
quantity,
|
|
order.min_quantity,
|
|
order.max_quantity
|
|
);
|
|
|
|
let _ = self
|
|
.taker
|
|
.send(maker_inc_connections::TakerMessage {
|
|
taker_id: self.taker_id,
|
|
msg: wire::MakerToTaker::RejectOrder(order.id),
|
|
})
|
|
.await;
|
|
|
|
self.complete(Completed::Rejected(order.id), ctx).await;
|
|
}
|
|
}
|
|
|
|
async fn stopping(&mut self, ctx: &mut xtra::Context<Self>) -> xtra::KeepRunning {
|
|
// inform other actors that we are stopping so that our
|
|
// address can be GCd from their AddressMaps
|
|
let me = ctx.address().expect("we are still alive");
|
|
|
|
for channel in self.on_stopping.iter() {
|
|
let _ = channel.send(Stopping { me: me.clone() }).await;
|
|
}
|
|
|
|
xtra::KeepRunning::StopAll
|
|
}
|
|
}
|
|
|
|
/// Message sent from the `maker_cfd::Actor` to the
|
|
/// `setup_maker::Actor` to inform that the maker user has accepted
|
|
/// the taker order request from the taker.
|
|
pub struct Accepted;
|
|
|
|
/// Message sent from the `maker_cfd::Actor` to the
|
|
/// `setup_maker::Actor` to inform that the maker user has rejected
|
|
/// the taker order request from the taker.
|
|
pub struct Rejected;
|
|
|
|
/// Message sent from the `setup_maker::Actor` to the
|
|
/// `maker_cfd::Actor` to notify that the contract setup has started.
|
|
pub struct Started(pub OrderId);
|
|
|
|
/// Message sent from the spawned task to `setup_maker::Actor` to
|
|
/// notify that the contract setup has finished successfully.
|
|
pub struct SetupSucceeded {
|
|
order_id: OrderId,
|
|
dlc: Dlc,
|
|
}
|
|
|
|
/// Message sent from the spawned task to `setup_maker::Actor` to
|
|
/// notify that the contract setup has failed.
|
|
pub struct SetupFailed {
|
|
order_id: OrderId,
|
|
error: anyhow::Error,
|
|
}
|
|
|
|
/// Message sent from the `setup_maker::Actor` to the
|
|
/// `maker_cfd::Actor` to notify that the contract setup has finished.
|
|
#[derive(Debug)]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum Completed {
|
|
Rejected(OrderId),
|
|
NewContract {
|
|
order_id: OrderId,
|
|
dlc: Dlc,
|
|
},
|
|
Failed {
|
|
order_id: OrderId,
|
|
error: anyhow::Error,
|
|
},
|
|
}
|
|
|
|
impl xtra::Message for Started {
|
|
type Result = ();
|
|
}
|
|
|
|
impl ActorName for Actor {
|
|
fn actor_name() -> String {
|
|
"Maker contract setup".to_string()
|
|
}
|
|
}
|
|
|