From cf08326c64f02864e3da64f46254d1141d2c8a00 Mon Sep 17 00:00:00 2001 From: Mariusz Date: Fri, 1 Oct 2021 13:25:47 +0930 Subject: [PATCH] Add a taker endpoint for initiating collaborative settlement (#175) * Add a taker endpoint for initiating collaborative settlement For now, only logs the proposal in the logs on the maker side --- daemon/src/maker_cfd.rs | 26 +++++++++++++++++++++++++- daemon/src/model/cfd.rs | 23 +++++++++++++++++++++++ daemon/src/routes_taker.rs | 18 +++++++++++++++++- daemon/src/taker.rs | 1 + daemon/src/taker_cfd.rs | 37 +++++++++++++++++++++++++++++++++++++ daemon/src/wire.rs | 15 ++++++++++++++- 6 files changed, 117 insertions(+), 3 deletions(-) diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 2b285a1..d77654f 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -4,7 +4,9 @@ use crate::db::{ load_cfd_by_order_id, load_order_by_id, }; use crate::maker_inc_connections::TakerCommand; -use crate::model::cfd::{Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId}; +use crate::model::cfd::{ + Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, SettlementProposal, +}; use crate::model::{TakerId, Usd}; use crate::monitor::MonitorParams; use crate::wallet::Wallet; @@ -142,6 +144,15 @@ impl Actor { Ok(()) } + async fn handle_propose_settlement(&mut self, proposal: SettlementProposal) -> Result<()> { + tracing::info!( + "Received settlement proposal from the taker: {:?}", + proposal + ); + // TODO: Handle the proposal + Ok(()) + } + async fn handle_inc_protocol_msg( &mut self, taker_id: TakerId, @@ -484,6 +495,19 @@ impl Handler for Actor { wire::TakerToMaker::TakeOrder { order_id, quantity } => { log_error!(self.handle_take_order(taker, order_id, quantity)) } + wire::TakerToMaker::ProposeSettlement { + order_id, + timestamp, + taker, + maker, + } => { + log_error!(self.handle_propose_settlement(SettlementProposal { + order_id, + timestamp, + taker, + maker + })) + } wire::TakerToMaker::Protocol(msg) => { log_error!(self.handle_inc_protocol_msg(taker, msg)) } diff --git a/daemon/src/model/cfd.rs b/daemon/src/model/cfd.rs index 71264f5..c6d8ad7 100644 --- a/daemon/src/model/cfd.rs +++ b/daemon/src/model/cfd.rs @@ -309,6 +309,15 @@ impl Display for CfdState { } } +/// Proposed collaborative settlement +#[derive(Debug, Clone)] +pub struct SettlementProposal { + pub order_id: OrderId, + pub timestamp: SystemTime, + pub taker: Amount, + pub maker: Amount, +} + /// Represents a cfd (including state) #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Cfd { @@ -356,6 +365,20 @@ impl Cfd { Ok(profit) } + #[allow(dead_code)] // Not used by all binaries. + pub fn calculate_settlement(&self, _current_price: Usd) -> Result { + // TODO: Calculate values for taker and maker + // For the time being, assume that everybody loses :) + let settlement = SettlementProposal { + order_id: self.order.id, + timestamp: SystemTime::now(), + taker: Amount::ZERO, + maker: Amount::ZERO, + }; + + Ok(settlement) + } + pub fn position(&self) -> Position { match self.order.origin { Origin::Ours => self.order.position.clone(), diff --git a/daemon/src/routes_taker.rs b/daemon/src/routes_taker.rs index ca3e339..5bd82a9 100644 --- a/daemon/src/routes_taker.rs +++ b/daemon/src/routes_taker.rs @@ -82,7 +82,7 @@ pub struct CfdOrderRequest { pub quantity: Usd, } -#[rocket::post("/cfd", data = "")] +#[rocket::post("/cfd/order", data = "")] pub async fn post_order_request( cfd_order_request: Json, cfd_actor_inbox: &State>, @@ -96,6 +96,22 @@ pub async fn post_order_request( .expect("actor to always be available"); } +#[rocket::post("/cfd//settle")] +pub async fn post_settlement_proposal( + id: OrderId, + cfd_actor_inbox: &State>, + quote_updates: &State>, +) { + let current_price = quote_updates.borrow().for_taker(); + cfd_actor_inbox + .do_send_async(taker_cfd::ProposeSettlement { + order_id: id, + current_price, + }) + .await + .expect("actor to always be available"); +} + #[rocket::get("/alive")] pub fn get_health_check() {} diff --git a/daemon/src/taker.rs b/daemon/src/taker.rs index 77f1f49..a7f6c4b 100644 --- a/daemon/src/taker.rs +++ b/daemon/src/taker.rs @@ -198,6 +198,7 @@ async fn main() -> Result<()> { rocket::routes![ routes_taker::feed, routes_taker::post_order_request, + routes_taker::post_settlement_proposal, routes_taker::get_health_check, routes_taker::margin_calc, ], diff --git a/daemon/src/taker_cfd.rs b/daemon/src/taker_cfd.rs index ba1b59a..645a450 100644 --- a/daemon/src/taker_cfd.rs +++ b/daemon/src/taker_cfd.rs @@ -26,6 +26,11 @@ pub struct TakeOffer { pub quantity: Usd, } +pub struct ProposeSettlement { + pub order_id: OrderId, + pub current_price: Usd, +} + pub struct MakerStreamMessage { pub item: Result, } @@ -121,6 +126,27 @@ impl Actor { Ok(()) } + async fn handle_propose_settlement( + &mut self, + order_id: OrderId, + current_price: Usd, + ) -> Result<()> { + let mut conn = self.db.acquire().await?; + let cfd = load_cfd_by_order_id(order_id, &mut conn).await?; + + let settlement = cfd.calculate_settlement(current_price)?; + + self.send_to_maker + .do_send_async(wire::TakerToMaker::ProposeSettlement { + order_id: settlement.order_id, + timestamp: settlement.timestamp, + taker: settlement.taker, + maker: settlement.maker, + }) + .await?; + Ok(()) + } + async fn handle_new_order(&mut self, order: Option) -> Result<()> { match order { Some(mut order) => { @@ -310,6 +336,13 @@ impl Handler for Actor { } } +#[async_trait] +impl Handler for Actor { + async fn handle(&mut self, msg: ProposeSettlement, _ctx: &mut Context) { + log_error!(self.handle_propose_settlement(msg.order_id, msg.current_price)); + } +} + #[async_trait] impl Handler for Actor { async fn handle( @@ -363,6 +396,10 @@ impl Message for TakeOffer { type Result = (); } +impl Message for ProposeSettlement { + type Result = (); +} + // this signature is a bit different because we use `Address::attach_stream` impl Message for MakerStreamMessage { type Result = KeepRunning; diff --git a/daemon/src/wire.rs b/daemon/src/wire.rs index bc9df88..2bba4fe 100644 --- a/daemon/src/wire.rs +++ b/daemon/src/wire.rs @@ -13,13 +13,25 @@ use serde::{Deserialize, Serialize}; use std::fmt; use std::marker::PhantomData; use std::ops::RangeInclusive; +use std::time::SystemTime; use tokio_util::codec::{Decoder, Encoder, LengthDelimitedCodec}; #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type", content = "payload")] #[allow(clippy::large_enum_variant)] pub enum TakerToMaker { - TakeOrder { order_id: OrderId, quantity: Usd }, + TakeOrder { + order_id: OrderId, + quantity: Usd, + }, + ProposeSettlement { + order_id: OrderId, + timestamp: SystemTime, + #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] + taker: Amount, + #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] + maker: Amount, + }, Protocol(SetupMsg), } @@ -27,6 +39,7 @@ impl fmt::Display for TakerToMaker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TakerToMaker::TakeOrder { .. } => write!(f, "TakeOrder"), + TakerToMaker::ProposeSettlement { .. } => write!(f, "ProposeSettlement"), TakerToMaker::Protocol(_) => write!(f, "Protocol"), } }