From b8e9ac6f1bf254eb8d9ac1f8893773f32078345d Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 5 Oct 2021 15:17:25 +1100 Subject: [PATCH 1/6] Introduce `OracleEventId` wrapper type --- daemon/src/model.rs | 3 +++ daemon/src/oracle.rs | 13 ++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/daemon/src/model.rs b/daemon/src/model.rs index 8daeaad..72151b9 100644 --- a/daemon/src/model.rs +++ b/daemon/src/model.rs @@ -105,3 +105,6 @@ pub struct WalletInfo { pub address: Address, pub last_updated_at: SystemTime, } + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct OracleEventId(pub String); diff --git a/daemon/src/oracle.rs b/daemon/src/oracle.rs index 0b951ef..95e5e15 100644 --- a/daemon/src/oracle.rs +++ b/daemon/src/oracle.rs @@ -1,4 +1,5 @@ use crate::actors::log_error; +use crate::model::OracleEventId; use anyhow::{Context, Result}; use async_trait::async_trait; use cfd_protocol::secp256k1_zkp::{schnorrsig, SecretKey}; @@ -153,7 +154,7 @@ where } pub struct MonitorEvent { - pub event_id: String, + pub event_id: OracleEventId, } impl xtra::Message for MonitorEvent { @@ -167,7 +168,7 @@ where M: xtra::Handler, { async fn handle(&mut self, msg: MonitorEvent, _ctx: &mut xtra::Context) { - self.monitor_event(msg.event_id) + self.monitor_event(msg.event_id.0) } } @@ -202,7 +203,7 @@ pub struct Announcement { /// /// Doubles up as the path of the URL for this event i.e. /// https://h00.ooo/{id}. - pub id: String, + pub id: OracleEventId, pub expected_outcome_time: OffsetDateTime, pub nonce_pks: Vec, } @@ -228,6 +229,7 @@ impl xtra::Message for Attestation { } mod olivia_api { + use crate::model::OracleEventId; use anyhow::Context; use cfd_protocol::secp256k1_zkp::{schnorrsig, SecretKey}; use std::convert::TryFrom; @@ -249,7 +251,7 @@ mod olivia_api { serde_json::from_str::(&response.announcement.oracle_event.data)?; Ok(Self { - id: data.id, + id: OracleEventId(data.id), expected_outcome_time: data.expected_outcome_time, nonce_pks: data.schemes.olivia_v1.nonces, }) @@ -338,6 +340,7 @@ mod olivia_api { mod tests { use std::vec; + use crate::model::OracleEventId; use crate::oracle; use time::macros::datetime; @@ -347,7 +350,7 @@ mod olivia_api { let deserialized = serde_json::from_str::(json).unwrap(); let expected = oracle::Announcement { - id: "/x/BitMEX/BXBT/2021-10-04T22:00:00.price[n:20]".to_string(), + id: OracleEventId("/x/BitMEX/BXBT/2021-10-04T22:00:00.price[n:20]".to_string()), expected_outcome_time: datetime!(2021-10-04 22:00:00).assume_utc(), nonce_pks: vec![ "8d72028eeaf4b85aec0f750f05a4a320cac193f5d8494bfe05cd4b29f3df4239" From da339bd1399143edceda90463a644277c5f1b4c1 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 5 Oct 2021 16:06:14 +1100 Subject: [PATCH 2/6] Maker publishes `OracleEventId` as part of offer The latest announcement known to the maker's cfd actor is used for extracting the event id. --- ...0903050345_create_cfd_and_order_tables.sql | 3 +- daemon/sqlx-data.json | 146 ++++++++++-------- daemon/src/db.rs | 46 ++++-- daemon/src/maker_cfd.rs | 31 +++- daemon/src/model/cfd.rs | 29 ++-- daemon/src/routes_maker.rs | 17 +- 6 files changed, 169 insertions(+), 103 deletions(-) diff --git a/daemon/migrations/20210903050345_create_cfd_and_order_tables.sql b/daemon/migrations/20210903050345_create_cfd_and_order_tables.sql index b317b44..a4904a6 100644 --- a/daemon/migrations/20210903050345_create_cfd_and_order_tables.sql +++ b/daemon/migrations/20210903050345_create_cfd_and_order_tables.sql @@ -12,7 +12,8 @@ create table if not exists orders liquidation_price text not null, creation_timestamp text not null, term text not null, - origin text not null + origin text not null, + oracle_event_id text not null ); create unique index if not exists orders_uuid diff --git a/daemon/sqlx-data.json b/daemon/sqlx-data.json index 7e1a6d7..3cd8045 100644 --- a/daemon/sqlx-data.json +++ b/daemon/sqlx-data.json @@ -1,35 +1,7 @@ { "db": "SQLite", - "3ec696a1077ae52f21230ac33b9083ae3420698ff4d6bb1bba2d71ad518daf85": { - "query": "\n insert into orders (\n uuid,\n trading_pair,\n position,\n initial_price,\n min_quantity,\n max_quantity,\n leverage,\n liquidation_price,\n creation_timestamp,\n term,\n origin\n ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 11 - }, - "nullable": [] - } - }, - "50abbb297394739ec9d85917f8c32aa8bcfa0bfe140b24e9eeda4ce8d30d4f8d": { - "query": "\n select\n state\n from cfd_states\n where cfd_id = ?\n order by id desc\n limit 1;\n ", - "describe": { - "columns": [ - { - "name": "state", - "ordinal": 0, - "type_info": "Text" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - false - ] - } - }, - "57c0eb6669321997352d87372431788aa039dd1898ca0b11ba4600f743ff4d93": { - "query": "\n select\n cfds.id as cfd_id,\n orders.uuid as order_id,\n orders.initial_price as price,\n orders.min_quantity as min_quantity,\n orders.max_quantity as max_quantity,\n orders.leverage as leverage,\n orders.trading_pair as trading_pair,\n orders.position as position,\n orders.origin as origin,\n orders.liquidation_price as liquidation_price,\n orders.creation_timestamp as creation_timestamp,\n orders.term as term,\n cfds.quantity_usd as quantity_usd,\n cfd_states.state as state\n from cfds as cfds\n inner join orders as orders on cfds.order_id = orders.id\n inner join cfd_states as cfd_states on cfd_states.cfd_id = cfds.id\n where cfd_states.state in (\n select\n state\n from cfd_states\n where cfd_id = cfds.id\n order by id desc\n limit 1\n )\n ", + "41fb3bea22bde82a79aee6096d579a16494a20cfcfb2e8cfffe0a56073460bbf": { + "query": "\n select\n cfds.id as cfd_id,\n orders.uuid as order_id,\n orders.initial_price as price,\n orders.min_quantity as min_quantity,\n orders.max_quantity as max_quantity,\n orders.leverage as leverage,\n orders.trading_pair as trading_pair,\n orders.position as position,\n orders.origin as origin,\n orders.liquidation_price as liquidation_price,\n orders.creation_timestamp as creation_timestamp,\n orders.term as term,\n orders.oracle_event_id,\n cfds.quantity_usd as quantity_usd,\n cfd_states.state as state\n from cfds as cfds\n inner join orders as orders on cfds.order_id = orders.id\n inner join cfd_states as cfd_states on cfd_states.cfd_id = cfds.id\n where cfd_states.state in (\n select\n state\n from cfd_states\n where cfd_id = cfds.id\n order by id desc\n limit 1\n )\n ", "describe": { "columns": [ { @@ -93,14 +65,19 @@ "type_info": "Text" }, { - "name": "quantity_usd", + "name": "oracle_event_id", "ordinal": 12, "type_info": "Text" }, { - "name": "state", + "name": "quantity_usd", "ordinal": 13, "type_info": "Text" + }, + { + "name": "state", + "ordinal": 14, + "type_info": "Text" } ], "parameters": { @@ -120,12 +97,69 @@ false, false, false, + false, + false + ] + } + }, + "50abbb297394739ec9d85917f8c32aa8bcfa0bfe140b24e9eeda4ce8d30d4f8d": { + "query": "\n select\n state\n from cfd_states\n where cfd_id = ?\n order by id desc\n limit 1;\n ", + "describe": { + "columns": [ + { + "name": "state", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ false ] } }, - "62c8df2dde7a305757a070149a7066faf15da1ef2d6c6fc4c0bd83e385e4750e": { - "query": "\n select\n cfds.id as cfd_id,\n orders.uuid as order_id,\n orders.initial_price as price,\n orders.min_quantity as min_quantity,\n orders.max_quantity as max_quantity,\n orders.leverage as leverage,\n orders.trading_pair as trading_pair,\n orders.position as position,\n orders.origin as origin,\n orders.liquidation_price as liquidation_price,\n orders.creation_timestamp as creation_timestamp,\n orders.term as term,\n cfds.quantity_usd as quantity_usd,\n cfd_states.state as state\n from cfds as cfds\n inner join orders as orders on cfds.order_id = orders.id\n inner join cfd_states as cfd_states on cfd_states.cfd_id = cfds.id\n where cfd_states.state in (\n select\n state\n from cfd_states\n where cfd_id = cfds.id\n order by id desc\n limit 1\n )\n and orders.uuid = ?\n ", + "8e7571250da58b12f5884f17656e5966957c7798ea029c701a4fc43fd613f015": { + "query": "\n select\n id\n from cfds\n where order_uuid = ?;\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + true + ] + } + }, + "a464a1feb12abadff8bfd5b2b3b7362f3846869c0702944b21737eff8f420be5": { + "query": "\n insert into cfd_states (\n cfd_id,\n state\n ) values (?, ?);\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + } + }, + "a59cf0824b5e4191c94c229086ce464d1b8084f65a6fafb165d27ce6df5b815b": { + "query": "\n insert into orders (\n uuid,\n trading_pair,\n position,\n initial_price,\n min_quantity,\n max_quantity,\n leverage,\n liquidation_price,\n creation_timestamp,\n term,\n origin,\n oracle_event_id\n ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 12 + }, + "nullable": [] + } + }, + "c404a4eebb7118fd4e716fe4155e011a52e12a3594b698ca4f0662674cd067f8": { + "query": "\n select\n cfds.id as cfd_id,\n orders.uuid as order_id,\n orders.initial_price as price,\n orders.min_quantity as min_quantity,\n orders.max_quantity as max_quantity,\n orders.leverage as leverage,\n orders.trading_pair as trading_pair,\n orders.position as position,\n orders.origin as origin,\n orders.liquidation_price as liquidation_price,\n orders.creation_timestamp as creation_timestamp,\n orders.term as term,\n orders.oracle_event_id,\n cfds.quantity_usd as quantity_usd,\n cfd_states.state as state\n from cfds as cfds\n inner join orders as orders on cfds.order_id = orders.id\n inner join cfd_states as cfd_states on cfd_states.cfd_id = cfds.id\n where cfd_states.state in (\n select\n state\n from cfd_states\n where cfd_id = cfds.id\n order by id desc\n limit 1\n )\n and orders.uuid = ?\n ", "describe": { "columns": [ { @@ -189,14 +223,19 @@ "type_info": "Text" }, { - "name": "quantity_usd", + "name": "oracle_event_id", "ordinal": 12, "type_info": "Text" }, { - "name": "state", + "name": "quantity_usd", "ordinal": 13, "type_info": "Text" + }, + { + "name": "state", + "ordinal": 14, + "type_info": "Text" } ], "parameters": { @@ -216,38 +255,11 @@ false, false, false, + false, false ] } }, - "8e7571250da58b12f5884f17656e5966957c7798ea029c701a4fc43fd613f015": { - "query": "\n select\n id\n from cfds\n where order_uuid = ?;\n ", - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int64" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - true - ] - } - }, - "a464a1feb12abadff8bfd5b2b3b7362f3846869c0702944b21737eff8f420be5": { - "query": "\n insert into cfd_states (\n cfd_id,\n state\n ) values (?, ?);\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 2 - }, - "nullable": [] - } - }, "f3dce76f316212c91cb3402b0cef00f1c9adbef8519c54e9bdbd373aab946209": { "query": "\n select * from orders where uuid = ?;\n ", "describe": { @@ -311,6 +323,11 @@ "name": "origin", "ordinal": 11, "type_info": "Text" + }, + { + "name": "oracle_event_id", + "ordinal": 12, + "type_info": "Text" } ], "parameters": { @@ -328,6 +345,7 @@ false, false, false, + false, false ] } diff --git a/daemon/src/db.rs b/daemon/src/db.rs index f5823d3..0f933e7 100644 --- a/daemon/src/db.rs +++ b/daemon/src/db.rs @@ -1,5 +1,5 @@ use crate::model::cfd::{Cfd, CfdState, Order, OrderId, Origin}; -use crate::model::{Leverage, Position}; +use crate::model::{Leverage, OracleEventId, Position}; use anyhow::{Context, Result}; use rocket_db_pools::sqlx; use sqlx::pool::PoolConnection; @@ -24,6 +24,7 @@ pub async fn insert_order(order: &Order, conn: &mut PoolConnection) -> a let creation_timestamp = serde_json::to_string(&order.creation_timestamp).unwrap(); let term = serde_json::to_string(&order.term).unwrap(); let origin = serde_json::to_string(&order.origin).unwrap(); + let oracle_event_id = order.oracle_event_id.0.clone(); sqlx::query!( r#" @@ -38,8 +39,9 @@ pub async fn insert_order(order: &Order, conn: &mut PoolConnection) -> a liquidation_price, creation_timestamp, term, - origin - ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + origin, + oracle_event_id + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); "#, uuid, trading_pair, @@ -51,7 +53,8 @@ pub async fn insert_order(order: &Order, conn: &mut PoolConnection) -> a liquidation_price, creation_timestamp, term, - origin + origin, + oracle_event_id ) .execute(conn) .await?; @@ -85,6 +88,7 @@ pub async fn load_order_by_id( let creation_timestamp = serde_json::from_str(row.creation_timestamp.as_str()).unwrap(); let term = serde_json::from_str(row.term.as_str()).unwrap(); let origin = serde_json::from_str(row.origin.as_str()).unwrap(); + let oracle_event_id = OracleEventId(row.oracle_event_id); Ok(Order { id: uuid, @@ -98,6 +102,7 @@ pub async fn load_order_by_id( creation_timestamp, term, origin, + oracle_event_id, }) } @@ -264,6 +269,7 @@ pub async fn load_cfd_by_order_id( orders.liquidation_price as liquidation_price, orders.creation_timestamp as creation_timestamp, orders.term as term, + orders.oracle_event_id, cfds.quantity_usd as quantity_usd, cfd_states.state as state from cfds as cfds @@ -295,6 +301,7 @@ pub async fn load_cfd_by_order_id( let creation_timestamp = serde_json::from_str(row.creation_timestamp.as_str()).unwrap(); let term = serde_json::from_str(row.term.as_str()).unwrap(); let origin: Origin = serde_json::from_str(row.origin.as_str()).unwrap(); + let oracle_event_id = OracleEventId(row.oracle_event_id.clone()); let quantity = serde_json::from_str(row.quantity_usd.as_str()).unwrap(); let latest_state = serde_json::from_str(row.state.as_str()).unwrap(); @@ -311,6 +318,7 @@ pub async fn load_cfd_by_order_id( creation_timestamp, term, origin, + oracle_event_id, }; Ok(Cfd { @@ -339,6 +347,7 @@ pub async fn load_all_cfds(conn: &mut PoolConnection) -> anyhow::Result< orders.liquidation_price as liquidation_price, orders.creation_timestamp as creation_timestamp, orders.term as term, + orders.oracle_event_id, cfds.quantity_usd as quantity_usd, cfd_states.state as state from cfds as cfds @@ -371,6 +380,7 @@ pub async fn load_all_cfds(conn: &mut PoolConnection) -> anyhow::Result< let creation_timestamp = serde_json::from_str(row.creation_timestamp.as_str()).unwrap(); let term = serde_json::from_str(row.term.as_str()).unwrap(); let origin: Origin = serde_json::from_str(row.origin.as_str()).unwrap(); + let oracle_event_id = OracleEventId(row.oracle_event_id.clone()); let quantity = serde_json::from_str(row.quantity_usd.as_str()).unwrap(); let latest_state = serde_json::from_str(row.state.as_str()).unwrap(); @@ -387,6 +397,7 @@ pub async fn load_all_cfds(conn: &mut PoolConnection) -> anyhow::Result< creation_timestamp, term, origin, + oracle_event_id, }; Cfd { @@ -420,7 +431,7 @@ mod tests { let pool = setup_test_db().await; let mut conn = pool.acquire().await.unwrap(); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); + let order = Order::default(); insert_order(&order, &mut conn).await.unwrap(); let order_loaded = load_order_by_id(order.id, &mut conn).await.unwrap(); @@ -433,7 +444,7 @@ mod tests { let pool = setup_test_db().await; let mut conn = pool.acquire().await.unwrap(); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); + let order = Order::default(); let cfd = Cfd::new( order.clone(), Usd(dec!(1000)), @@ -457,7 +468,7 @@ mod tests { let pool = setup_test_db().await; let mut conn = pool.acquire().await.unwrap(); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); + let order = Order::default(); let cfd = Cfd::new( order.clone(), Usd(dec!(1000)), @@ -482,8 +493,7 @@ mod tests { let pool = setup_test_db().await; let mut conn = pool.acquire().await.unwrap(); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); - + let order = Order::default(); let cfd = Cfd::new( order.clone(), Usd(dec!(1000)), @@ -502,8 +512,7 @@ mod tests { let cfd_from_db = load_cfd_by_order_id(order_id, &mut conn).await.unwrap(); assert_eq!(cfd, cfd_from_db); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); - + let order = Order::default(); let cfd = Cfd::new( order.clone(), Usd(dec!(1000)), @@ -528,7 +537,7 @@ mod tests { let pool = setup_test_db().await; let mut conn = pool.acquire().await.unwrap(); - let order = Order::from_default_with_price(Usd(dec!(10000)), Origin::Theirs).unwrap(); + let order = Order::default(); let mut cfd = Cfd::new( order.clone(), Usd(dec!(1000)), @@ -572,4 +581,17 @@ mod tests { pool } + + impl Default for Order { + fn default() -> Self { + Order::new( + Usd(dec!(1000)), + Usd(dec!(100)), + Usd(dec!(1000)), + Origin::Theirs, + OracleEventId("Dummy".to_string()), + ) + .unwrap() + } + } } diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 7c1f131..2a9374e 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -5,7 +5,7 @@ use crate::db::{ }; use crate::maker_inc_connections::TakerCommand; use crate::model::cfd::{ - Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Role, + Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role, SettlementProposal, SettlementProposals, }; use crate::model::{TakerId, Usd}; @@ -43,7 +43,11 @@ pub struct RejectSettlement { pub order_id: OrderId, } -pub struct NewOrder(pub Order); +pub struct NewOrder { + pub price: Usd, + pub min_quantity: Usd, + pub max_quantity: Usd, +} pub struct NewTakerOnline { pub id: TakerId, @@ -121,7 +125,26 @@ impl Actor { ))?) } - async fn handle_new_order(&mut self, order: Order) -> Result<()> { + async fn handle_new_order( + &mut self, + price: Usd, + min_quantity: Usd, + max_quantity: Usd, + ) -> Result<()> { + let oracle_event_id = if let Some(latest_announcement) = self.latest_announcement.clone() { + latest_announcement.id + } else { + bail!("Cannot create order because no announcement from oracle") + }; + + let order = Order::new( + price, + min_quantity, + max_quantity, + Origin::Ours, + oracle_event_id, + )?; + // 1. Save to DB let mut conn = self.db.acquire().await?; insert_order(&order, &mut conn).await?; @@ -565,7 +588,7 @@ impl Handler for Actor { #[async_trait] impl Handler for Actor { async fn handle(&mut self, msg: NewOrder, _ctx: &mut Context) { - log_error!(self.handle_new_order(msg.0)); + log_error!(self.handle_new_order(msg.price, msg.min_quantity, msg.max_quantity)); } } diff --git a/daemon/src/model/cfd.rs b/daemon/src/model/cfd.rs index 3f60c84..8347a94 100644 --- a/daemon/src/model/cfd.rs +++ b/daemon/src/model/cfd.rs @@ -1,4 +1,4 @@ -use crate::model::{Leverage, Percent, Position, TakerId, TradingPair, Usd}; +use crate::model::{Leverage, OracleEventId, Percent, Position, TakerId, TradingPair, Usd}; use crate::monitor; use anyhow::{bail, Context, Result}; use bdk::bitcoin::secp256k1::{SecretKey, Signature}; @@ -91,11 +91,22 @@ pub struct Order { pub term: Duration, pub origin: Origin, + + /// The id of the event to be used for price attestation + /// + /// The maker includes this into the Order based on the Oracle announcement to be used. + pub oracle_event_id: OracleEventId, } #[allow(dead_code)] // Only one binary and the tests use this. impl Order { - pub fn from_default_with_price(price: Usd, origin: Origin) -> Result { + pub fn new( + price: Usd, + min_quantity: Usd, + max_quantity: Usd, + origin: Origin, + oracle_event_id: OracleEventId, + ) -> Result { let leverage = Leverage(5); let maintenance_margin_rate = dec!(0.005); let liquidation_price = @@ -104,8 +115,8 @@ impl Order { Ok(Order { id: OrderId::default(), price, - min_quantity: Usd(dec!(1000)), - max_quantity: Usd(dec!(10000)), + min_quantity, + max_quantity, leverage, trading_pair: TradingPair::BtcUsd, liquidation_price, @@ -113,17 +124,9 @@ impl Order { creation_timestamp: SystemTime::now(), term: Duration::from_secs(60 * 60 * 8), // 8 hours origin, + oracle_event_id, }) } - pub fn with_min_quantity(mut self, min_quantity: Usd) -> Order { - self.min_quantity = min_quantity; - self - } - - pub fn with_max_quantity(mut self, max_quantity: Usd) -> Order { - self.max_quantity = max_quantity; - self - } } fn calculate_liquidation_price( diff --git a/daemon/src/routes_maker.rs b/daemon/src/routes_maker.rs index c283231..409b989 100644 --- a/daemon/src/routes_maker.rs +++ b/daemon/src/routes_maker.rs @@ -1,5 +1,5 @@ use crate::auth::Authenticated; -use crate::model::cfd::{Cfd, Order, OrderId, Origin, Role, SettlementProposals}; +use crate::model::cfd::{Cfd, Order, OrderId, Role, SettlementProposals}; use crate::model::{Usd, WalletInfo}; use crate::routes::EmbeddedFileExt; use crate::to_sse_event::{CfdAction, CfdsWithAuxData, ToSseEvent}; @@ -87,16 +87,15 @@ pub async fn post_sell_order( order: Json, cfd_actor_address: &State>, _auth: Authenticated, -) -> Result, status::BadRequest> { - let order = Order::from_default_with_price(order.price, Origin::Ours) - .map_err(|e| status::BadRequest(Some(e.to_string())))? - .with_min_quantity(order.min_quantity) - .with_max_quantity(order.max_quantity); - +) -> Result, Status> { cfd_actor_address - .do_send_async(maker_cfd::NewOrder(order)) + .do_send_async(maker_cfd::NewOrder { + price: order.price, + min_quantity: order.min_quantity, + max_quantity: order.max_quantity, + }) .await - .expect("actor to always be available"); + .map_err(|_| Status::new(500))?; Ok(status::Accepted(None)) } From 61d62e72630e82a39dae53eac9375a9f20c5e477 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 5 Oct 2021 20:31:47 +1100 Subject: [PATCH 3/6] Taker uses latest announcements from oracle to start contract setup Opted not to implement an additional message to fetch a specific announcement from the Oracle actor because it would have made the oracle actor unnecessarily complex (We would have to split it into different functionality by trait). The taker cfd actor holds a sliding window of all the announcements received from the oracle actor. --- daemon/src/maker_cfd.rs | 2 +- daemon/src/oracle.rs | 1 + daemon/src/taker_cfd.rs | 38 +++++++++++++++++++++----------------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 2a9374e..85dae13 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -12,7 +12,7 @@ use crate::model::{TakerId, Usd}; use crate::monitor::MonitorParams; use crate::wallet::Wallet; use crate::{maker_inc_connections, monitor, oracle, setup_contract, wire}; -use anyhow::{Context as _, Result}; +use anyhow::{bail, Context as _, Result}; use async_trait::async_trait; use bdk::bitcoin::secp256k1::schnorrsig; use futures::channel::mpsc; diff --git a/daemon/src/oracle.rs b/daemon/src/oracle.rs index 95e5e15..38fc04a 100644 --- a/daemon/src/oracle.rs +++ b/daemon/src/oracle.rs @@ -208,6 +208,7 @@ pub struct Announcement { pub nonce_pks: Vec, } +#[derive(Debug, Clone)] pub struct Announcements(pub [Announcement; 24]); // TODO: Implement real deserialization once price attestation is diff --git a/daemon/src/taker_cfd.rs b/daemon/src/taker_cfd.rs index ad92ca7..8a056c8 100644 --- a/daemon/src/taker_cfd.rs +++ b/daemon/src/taker_cfd.rs @@ -63,8 +63,8 @@ pub struct Actor { send_to_maker: Address>, monitor_actor: Address>, setup_state: SetupState, - latest_announcement: Option, - _oracle_actor: Address>>, + latest_announcements: Option, + oracle_actor: Address>>, current_settlement_proposals: HashMap, } @@ -91,8 +91,8 @@ impl Actor { send_to_maker, monitor_actor, setup_state: SetupState::None, - latest_announcement: None, - _oracle_actor: oracle_actor, + oracle_actor, + latest_announcements: None, current_settlement_proposals: HashMap::new(), } } @@ -213,18 +213,23 @@ impl Actor { .send(load_all_cfds(&mut conn).await?)?; let cfd = load_cfd_by_order_id(order_id, &mut conn).await?; - // let latest_announcement = self - // .latest_announcement - // .to_owned() - // .context("Unaware of oracle's latest announcement.")?; - - // self.oracle_actor - // .do_send_async(oracle::MonitorEvent { - // event_id: latest_announcement.id, - // }) - // .await?; + let offer_announcements = self + .latest_announcements + .clone() + .context("No oracle announcements available")?; + let offer_announcement = offer_announcements + .0 + .iter() + .find(|announcement| announcement.id == cfd.order.oracle_event_id) + .context("Order's announcement not found in list of current oracle announcements")?; + + self.oracle_actor + .do_send_async(oracle::MonitorEvent { + event_id: offer_announcement.id.clone(), + }) + .await?; - let nonce_pks = Vec::new(); + let nonce_pks = offer_announcement.nonce_pks.clone(); let contract_future = setup_contract::new( self.send_to_maker @@ -392,8 +397,7 @@ impl Actor { &mut self, announcements: oracle::Announcements, ) -> Result<()> { - tracing::debug!("Updating latest oracle announcements"); - self.latest_announcement = Some(announcements.0.last().unwrap().clone()); + self.latest_announcements = Some(announcements); Ok(()) } From 5842ff37a84e030df28dca2fe7274adb62924c24 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 5 Oct 2021 21:40:48 +1100 Subject: [PATCH 4/6] Maker uses latest announcements from oracle in contract setup --- daemon/src/maker_cfd.rs | 52 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 85dae13..42e8526 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -74,8 +74,8 @@ pub struct Actor { current_order_id: Option, monitor_actor: Address>, setup_state: SetupState, - latest_announcement: Option, - _oracle_actor: Address>>, + latest_announcements: Option, + oracle_actor: Address>>, current_settlement_proposals: HashMap, } @@ -111,8 +111,8 @@ impl Actor { current_order_id: None, monitor_actor, setup_state: SetupState::None, - latest_announcement: None, - _oracle_actor: oracle_actor, + latest_announcements: None, + oracle_actor, current_settlement_proposals: HashMap::new(), } } @@ -131,11 +131,16 @@ impl Actor { min_quantity: Usd, max_quantity: Usd, ) -> Result<()> { - let oracle_event_id = if let Some(latest_announcement) = self.latest_announcement.clone() { - latest_announcement.id - } else { - bail!("Cannot create order because no announcement from oracle") - }; + let oracle_event_id = self + .latest_announcements + .clone() + .context("Cannot create order because no announcement from oracle")? + .0 + .iter() + .last() + .context("Empty list of announcements")? + .clone() + .id; let order = Order::new( price, @@ -363,18 +368,23 @@ impl Actor { self.cfd_feed_actor_inbox .send(load_all_cfds(&mut conn).await?)?; - // let latest_announcement = self - // .latest_announcement - // .to_owned() - // .context("Unaware of oracle's latest announcement.")?; - - // self.oracle_actor - // .do_send_async(oracle::MonitorEvent { - // event_id: latest_announcement.id, - // }) - // .await?; + let offer_announcements = self + .latest_announcements + .clone() + .context("No oracle announcements available")?; + let offer_announcement = offer_announcements + .0 + .iter() + .find(|announcement| announcement.id == cfd.order.oracle_event_id) + .context("Order's announcement not found in list of current oracle announcements")?; + + self.oracle_actor + .do_send_async(oracle::MonitorEvent { + event_id: offer_announcement.id.clone(), + }) + .await?; - let nonce_pks = Vec::new(); + let nonce_pks = offer_announcement.nonce_pks.clone(); let contract_future = setup_contract::new( self.takers.clone().into_sink().with(move |msg| { @@ -533,7 +543,7 @@ impl Actor { announcements: oracle::Announcements, ) -> Result<()> { tracing::debug!("Updating latest oracle announcements"); - self.latest_announcement = Some(announcements.0.last().unwrap().clone()); + self.latest_announcements = Some(announcements); Ok(()) } From 8a2f2b83ccb580a6cb883330ea421d6da559d060 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Wed, 6 Oct 2021 10:38:29 +1100 Subject: [PATCH 5/6] `BTreeMap` for announcements in cfd actors Replaces the option on ever update by mapping the oracle's announcement array into a BTreeMap. --- daemon/src/maker_cfd.rs | 30 +++++++++++++++++------------- daemon/src/model.rs | 2 +- daemon/src/taker_cfd.rs | 20 ++++++++++++-------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 42e8526..85818ee 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -8,16 +8,16 @@ use crate::model::cfd::{ Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role, SettlementProposal, SettlementProposals, }; -use crate::model::{TakerId, Usd}; +use crate::model::{OracleEventId, TakerId, Usd}; use crate::monitor::MonitorParams; use crate::wallet::Wallet; use crate::{maker_inc_connections, monitor, oracle, setup_contract, wire}; -use anyhow::{bail, Context as _, Result}; +use anyhow::{Context as _, Result}; use async_trait::async_trait; use bdk::bitcoin::secp256k1::schnorrsig; use futures::channel::mpsc; use futures::{future, SinkExt}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::time::SystemTime; use tokio::sync::watch; use xtra::prelude::*; @@ -74,7 +74,7 @@ pub struct Actor { current_order_id: Option, monitor_actor: Address>, setup_state: SetupState, - latest_announcements: Option, + latest_announcements: Option>, oracle_actor: Address>>, current_settlement_proposals: HashMap, } @@ -135,12 +135,11 @@ impl Actor { .latest_announcements .clone() .context("Cannot create order because no announcement from oracle")? - .0 .iter() - .last() + .next_back() .context("Empty list of announcements")? - .clone() - .id; + .0 + .clone(); let order = Order::new( price, @@ -373,10 +372,8 @@ impl Actor { .clone() .context("No oracle announcements available")?; let offer_announcement = offer_announcements - .0 - .iter() - .find(|announcement| announcement.id == cfd.order.oracle_event_id) - .context("Order's announcement not found in list of current oracle announcements")?; + .get(&cfd.order.oracle_event_id) + .context("Order's announcement not found in current oracle announcements")?; self.oracle_actor .do_send_async(oracle::MonitorEvent { @@ -543,7 +540,14 @@ impl Actor { announcements: oracle::Announcements, ) -> Result<()> { tracing::debug!("Updating latest oracle announcements"); - self.latest_announcements = Some(announcements); + + self.latest_announcements.replace( + announcements + .0 + .iter() + .map(|announcement| (announcement.id.clone(), announcement.clone())) + .collect(), + ); Ok(()) } diff --git a/daemon/src/model.rs b/daemon/src/model.rs index 72151b9..0024a15 100644 --- a/daemon/src/model.rs +++ b/daemon/src/model.rs @@ -106,5 +106,5 @@ pub struct WalletInfo { pub last_updated_at: SystemTime, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct OracleEventId(pub String); diff --git a/daemon/src/taker_cfd.rs b/daemon/src/taker_cfd.rs index 8a056c8..265ff02 100644 --- a/daemon/src/taker_cfd.rs +++ b/daemon/src/taker_cfd.rs @@ -7,7 +7,7 @@ use crate::model::cfd::{ Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role, SettlementProposal, SettlementProposals, }; -use crate::model::Usd; +use crate::model::{OracleEventId, Usd}; use crate::monitor::{self, MonitorParams}; use crate::wallet::Wallet; use crate::wire::SetupMsg; @@ -17,7 +17,7 @@ use async_trait::async_trait; use bdk::bitcoin::secp256k1::schnorrsig; use futures::channel::mpsc; use futures::{future, SinkExt}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::time::SystemTime; use tokio::sync::watch; use xtra::prelude::*; @@ -63,7 +63,7 @@ pub struct Actor { send_to_maker: Address>, monitor_actor: Address>, setup_state: SetupState, - latest_announcements: Option, + latest_announcements: Option>, oracle_actor: Address>>, current_settlement_proposals: HashMap, } @@ -218,10 +218,8 @@ impl Actor { .clone() .context("No oracle announcements available")?; let offer_announcement = offer_announcements - .0 - .iter() - .find(|announcement| announcement.id == cfd.order.oracle_event_id) - .context("Order's announcement not found in list of current oracle announcements")?; + .get(&cfd.order.oracle_event_id) + .context("Order's announcement not found in current oracle announcements")?; self.oracle_actor .do_send_async(oracle::MonitorEvent { @@ -397,7 +395,13 @@ impl Actor { &mut self, announcements: oracle::Announcements, ) -> Result<()> { - self.latest_announcements = Some(announcements); + self.latest_announcements.replace( + announcements + .0 + .iter() + .map(|announcement| (announcement.id.clone(), announcement.clone())) + .collect(), + ); Ok(()) } From 8cc20534d4ee021af3d6366a241597eec8661527 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Wed, 6 Oct 2021 10:44:23 +1100 Subject: [PATCH 6/6] Remove debug message --- daemon/src/maker_cfd.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 85818ee..d23ad4b 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -539,8 +539,6 @@ impl Actor { &mut self, announcements: oracle::Announcements, ) -> Result<()> { - tracing::debug!("Updating latest oracle announcements"); - self.latest_announcements.replace( announcements .0