Browse Source

Merge pull request #80 from comit-network/past-party-param-build

Use the actual margin
no-contract-setup-message
Daniel Karzel 3 years ago
committed by GitHub
parent
commit
bde11ba368
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 96
      daemon/sqlx-data.json
  2. 106
      daemon/src/db.rs
  3. 14
      daemon/src/maker_cfd_actor.rs
  4. 39
      daemon/src/model/cfd.rs
  5. 10
      daemon/src/setup_contract_actor.rs
  6. 13
      daemon/src/taker_cfd_actor.rs
  7. 4
      daemon/src/to_sse_event.rs

96
daemon/sqlx-data.json

@ -152,6 +152,102 @@
"nullable": [] "nullable": []
} }
}, },
"a8454a2e4288b58d625f10722c6e23a51d5d17e6bb7af9685bcdbe64546969fa": {
"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_uuid = ?\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": [
{
"name": "cfd_id",
"ordinal": 0,
"type_info": "Int64"
},
{
"name": "order_id",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "price",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "min_quantity",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "max_quantity",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "leverage",
"ordinal": 5,
"type_info": "Int64"
},
{
"name": "trading_pair",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "position",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "origin",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "liquidation_price",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "creation_timestamp",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "term",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "quantity_usd",
"ordinal": 12,
"type_info": "Text"
},
{
"name": "state",
"ordinal": 13,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
]
}
},
"f3dce76f316212c91cb3402b0cef00f1c9adbef8519c54e9bdbd373aab946209": { "f3dce76f316212c91cb3402b0cef00f1c9adbef8519c54e9bdbd373aab946209": {
"query": "\n select * from orders where uuid = ?;\n ", "query": "\n select * from orders where uuid = ?;\n ",
"describe": { "describe": {

106
daemon/src/db.rs

@ -1,8 +1,7 @@
use crate::model::cfd::{Cfd, CfdState, Order, OrderId, Origin}; use crate::model::cfd::{Cfd, CfdState, Order, OrderId, Origin};
use crate::model::{Leverage, Position}; use crate::model::{Leverage, Position};
use anyhow::Context; use anyhow::{Context, Result};
use rocket_db_pools::sqlx; use rocket_db_pools::sqlx;
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
use sqlx::{Acquire, Sqlite, SqlitePool}; use sqlx::{Acquire, Sqlite, SqlitePool};
use std::convert::TryInto; use std::convert::TryInto;
@ -241,6 +240,82 @@ async fn load_latest_cfd_state(
Ok(latest_cfd_state_in_db) Ok(latest_cfd_state_in_db)
} }
pub async fn load_cfd_by_order_id(
order_id: OrderId,
conn: &mut PoolConnection<Sqlite>,
) -> Result<Cfd> {
let order_uuid = serde_json::to_string(&order_id)?;
let row = sqlx::query!(
r#"
select
cfds.id as cfd_id,
orders.uuid as order_id,
orders.initial_price as price,
orders.min_quantity as min_quantity,
orders.max_quantity as max_quantity,
orders.leverage as leverage,
orders.trading_pair as trading_pair,
orders.position as position,
orders.origin as origin,
orders.liquidation_price as liquidation_price,
orders.creation_timestamp as creation_timestamp,
orders.term as term,
cfds.quantity_usd as quantity_usd,
cfd_states.state as state
from cfds as cfds
inner join orders as orders on cfds.order_uuid = ?
inner join cfd_states as cfd_states on cfd_states.cfd_id = cfds.id
where cfd_states.state in (
select
state
from cfd_states
where cfd_id = cfds.id
order by id desc
limit 1
)
"#,
order_uuid
)
.fetch_one(conn)
.await?;
let order_id = serde_json::from_str(row.order_id.as_str()).unwrap();
let trading_pair = serde_json::from_str(row.trading_pair.as_str()).unwrap();
let position: Position = serde_json::from_str(row.position.as_str()).unwrap();
let price = serde_json::from_str(row.price.as_str()).unwrap();
let min_quantity = serde_json::from_str(row.min_quantity.as_str()).unwrap();
let max_quantity = serde_json::from_str(row.max_quantity.as_str()).unwrap();
let leverage = Leverage(row.leverage.try_into().unwrap());
let liquidation_price = serde_json::from_str(row.liquidation_price.as_str()).unwrap();
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 quantity = serde_json::from_str(row.quantity_usd.as_str()).unwrap();
let latest_state = serde_json::from_str(row.state.as_str()).unwrap();
let order = Order {
id: order_id,
trading_pair,
position,
price,
min_quantity,
max_quantity,
leverage,
liquidation_price,
creation_timestamp,
term,
origin,
};
Ok(Cfd {
order,
quantity_usd: quantity,
state: latest_state,
})
}
/// Loads all CFDs with the latest state as the CFD state /// Loads all CFDs with the latest state as the CFD state
pub async fn load_all_cfds(conn: &mut PoolConnection<Sqlite>) -> anyhow::Result<Vec<Cfd>> { pub async fn load_all_cfds(conn: &mut PoolConnection<Sqlite>) -> anyhow::Result<Vec<Cfd>> {
// TODO: Could be optimized with something like but not sure it's worth the complexity: // TODO: Could be optimized with something like but not sure it's worth the complexity:
@ -365,7 +440,6 @@ mod tests {
}, },
); );
// the order ahs to exist in the db in order to be able to insert the cfd
insert_order(&order, &mut conn).await.unwrap(); insert_order(&order, &mut conn).await.unwrap();
insert_cfd(cfd.clone(), &mut conn).await.unwrap(); insert_cfd(cfd.clone(), &mut conn).await.unwrap();
@ -374,6 +448,31 @@ mod tests {
assert_eq!(cfd, cfd_from_db) assert_eq!(cfd, cfd_from_db)
} }
#[tokio::test]
async fn test_insert_and_load_cfd_by_order_id() {
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 cfd = Cfd::new(
order.clone(),
Usd(dec!(1000)),
CfdState::PendingTakeRequest {
common: CfdStateCommon {
transition_timestamp: SystemTime::now(),
},
},
);
let order_id = order.id;
insert_order(&order, &mut conn).await.unwrap();
insert_cfd(cfd.clone(), &mut conn).await.unwrap();
let cfd_from_db = load_cfd_by_order_id(order_id, &mut conn).await.unwrap();
assert_eq!(cfd, cfd_from_db)
}
#[tokio::test] #[tokio::test]
async fn test_insert_new_cfd_state() { async fn test_insert_new_cfd_state() {
let pool = setup_test_db().await; let pool = setup_test_db().await;
@ -390,7 +489,6 @@ mod tests {
}, },
); );
// the order ahs to exist in the db in order to be able to insert the cfd
insert_order(&order, &mut conn).await.unwrap(); insert_order(&order, &mut conn).await.unwrap();
insert_cfd(cfd.clone(), &mut conn).await.unwrap(); insert_cfd(cfd.clone(), &mut conn).await.unwrap();

14
daemon/src/maker_cfd_actor.rs

@ -1,14 +1,12 @@
use std::time::SystemTime; use crate::db::{insert_cfd, insert_order, load_all_cfds, load_cfd_by_order_id, load_order_by_id};
use crate::db::{insert_cfd, insert_order, load_all_cfds, load_order_by_id};
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId}; use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId};
use crate::model::{TakerId, Usd}; use crate::model::{TakerId, Usd};
use crate::wallet::Wallet; use crate::wallet::Wallet;
use crate::wire::SetupMsg; use crate::wire::SetupMsg;
use crate::{maker_cfd_actor, maker_inc_connections_actor, setup_contract_actor}; use crate::{maker_cfd_actor, maker_inc_connections_actor, setup_contract_actor};
use bdk::bitcoin::secp256k1::schnorrsig; use bdk::bitcoin::secp256k1::schnorrsig;
use bdk::bitcoin::{self};
use futures::Future; use futures::Future;
use std::time::SystemTime;
use tokio::sync::{mpsc, watch}; use tokio::sync::{mpsc, watch};
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@ -156,12 +154,10 @@ pub fn new(
// Kick-off the CFD protocol // Kick-off the CFD protocol
let (sk, pk) = crate::keypair::new(&mut rand::thread_rng()); let (sk, pk) = crate::keypair::new(&mut rand::thread_rng());
// TODO: Load correct quantity from DB with order_id let cfd = load_cfd_by_order_id(order_id, &mut conn).await.unwrap();
let maker_params = wallet let margin = cfd.margin().unwrap();
.build_party_params(bitcoin::Amount::ZERO, pk)
.unwrap();
let cfd = load_order_by_id(order_id, &mut conn).await.unwrap(); let maker_params = wallet.build_party_params(margin, pk).unwrap();
let (actor, inbox) = setup_contract_actor::new( let (actor, inbox) = setup_contract_actor::new(
{ {

39
daemon/src/model/cfd.rs

@ -266,7 +266,7 @@ impl Cfd {
} }
} }
pub fn calc_margin(&self) -> Result<Amount> { pub fn margin(&self) -> Result<Amount> {
let margin = match self.position() { let margin = match self.position() {
Position::Buy => { Position::Buy => {
calculate_buy_margin(self.order.price, self.quantity_usd, self.order.leverage)? calculate_buy_margin(self.order.price, self.quantity_usd, self.order.leverage)?
@ -277,7 +277,18 @@ impl Cfd {
Ok(margin) Ok(margin)
} }
pub fn calc_profit(&self, current_price: Usd) -> Result<(Amount, Usd)> { pub fn counterparty_margin(&self) -> Result<Amount> {
let margin = match self.position() {
Position::Buy => calculate_sell_margin(self.order.price, self.quantity_usd)?,
Position::Sell => {
calculate_buy_margin(self.order.price, self.quantity_usd, self.order.leverage)?
}
};
Ok(margin)
}
pub fn profit(&self, current_price: Usd) -> Result<(Amount, Usd)> {
let profit = let profit =
calculate_profit(self.order.price, current_price, dec!(0.005), Usd(dec!(0.1)))?; calculate_profit(self.order.price, current_price, dec!(0.005), Usd(dec!(0.1)))?;
Ok(profit) Ok(profit)
@ -294,6 +305,30 @@ impl Cfd {
}, },
} }
} }
#[allow(dead_code)]
pub fn refund_timelock_in_blocks(&self) -> u32 {
self.order
.term
.mul_f32(Cfd::REFUND_THRESHOLD)
.as_blocks()
.ceil() as u32
}
/// A factor to be added to the CFD order term for calculating the refund timelock.
///
/// The refund timelock is important in case the oracle disappears or never publishes a
/// signature. Ideally, both users collaboratively settle in the refund scenario. This
/// factor is important if the users do not settle collaboratively.
/// `1.5` times the term as defined in CFD order should be safe in the extreme case where a user
/// publishes the commit transaction right after the contract was initialized. In this case, the
/// oracle still has `1.0 * cfdorder.term` time to attest and no one can publish the refund
/// transaction.
/// The downside is that if the oracle disappears: the users would only notice at the end
/// of the cfd term. In this case the users has to wait for another `1.5` times of the
/// term to get his funds back.
#[allow(dead_code)]
const REFUND_THRESHOLD: f32 = 1.5;
} }
fn calculate_profit( fn calculate_profit(

10
daemon/src/setup_contract_actor.rs

@ -1,4 +1,4 @@
use crate::model::cfd::{AsBlocks, FinalizedCfd, Order}; use crate::model::cfd::{AsBlocks, Cfd, FinalizedCfd};
use crate::wire::{AdaptorSignature, Msg0, Msg1, SetupMsg}; use crate::wire::{AdaptorSignature, Msg0, Msg1, SetupMsg};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use bdk::bitcoin::secp256k1::{schnorrsig, SecretKey, Signature, SECP256K1}; use bdk::bitcoin::secp256k1::{schnorrsig, SecretKey, Signature, SECP256K1};
@ -37,7 +37,7 @@ pub fn new(
own_params: OwnParams, own_params: OwnParams,
sk: SecretKey, sk: SecretKey,
oracle_pk: schnorrsig::PublicKey, oracle_pk: schnorrsig::PublicKey,
order: Order, cfd: Cfd,
) -> ( ) -> (
impl Future<Output = FinalizedCfd>, impl Future<Output = FinalizedCfd>,
mpsc::UnboundedSender<SetupMsg>, mpsc::UnboundedSender<SetupMsg>,
@ -66,11 +66,15 @@ pub fn new(
AllParams::new(own, own_punish, other, other_punish, own_role) AllParams::new(own, own_punish, other, other_punish, own_role)
}; };
if params.other.lock_amount != cfd.counterparty_margin().unwrap() {
panic!("Sorry, have to panic 😬 - the amounts that the counterparty sent were wrong, expected {} actual {}", cfd.counterparty_margin().unwrap(), params.other.lock_amount)
}
let own_cfd_txs = create_cfd_transactions( let own_cfd_txs = create_cfd_transactions(
(params.maker().clone(), *params.maker_punish()), (params.maker().clone(), *params.maker_punish()),
(params.taker().clone(), *params.taker_punish()), (params.taker().clone(), *params.taker_punish()),
oracle_pk, oracle_pk,
order.term.mul_f32(REFUND_THRESHOLD).as_blocks().ceil() as u32, cfd.order.term.mul_f32(REFUND_THRESHOLD).as_blocks().ceil() as u32,
vec![], vec![],
sk, sk,
) )

13
daemon/src/taker_cfd_actor.rs

@ -1,5 +1,6 @@
use crate::db::{ use crate::db::{
insert_cfd, insert_new_cfd_state_by_order_id, insert_order, load_all_cfds, load_order_by_id, insert_cfd, insert_new_cfd_state_by_order_id, insert_order, load_all_cfds,
load_cfd_by_order_id, load_order_by_id,
}; };
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId}; use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId};
use crate::model::Usd; use crate::model::Usd;
@ -7,7 +8,6 @@ use crate::wallet::Wallet;
use crate::wire::SetupMsg; use crate::wire::SetupMsg;
use crate::{setup_contract_actor, wire}; use crate::{setup_contract_actor, wire};
use bdk::bitcoin::secp256k1::schnorrsig; use bdk::bitcoin::secp256k1::schnorrsig;
use bdk::bitcoin::{self};
use core::panic; use core::panic;
use futures::Future; use futures::Future;
use std::time::SystemTime; use std::time::SystemTime;
@ -101,11 +101,10 @@ pub fn new(
let (sk, pk) = crate::keypair::new(&mut rand::thread_rng()); let (sk, pk) = crate::keypair::new(&mut rand::thread_rng());
let taker_params = wallet let cfd = load_cfd_by_order_id(order_id, &mut conn).await.unwrap();
.build_party_params(bitcoin::Amount::ZERO, pk) // TODO: Load correct quantity from DB let margin = cfd.margin().unwrap();
.unwrap();
let cfd = load_order_by_id(order_id, &mut conn).await.unwrap(); let taker_params = wallet.build_party_params(margin, pk).unwrap();
let (actor, inbox) = setup_contract_actor::new( let (actor, inbox) = setup_contract_actor::new(
{ {
@ -139,6 +138,8 @@ pub fn new(
} }
Command::CfdSetupCompleted(_finalized_cfd) => { Command::CfdSetupCompleted(_finalized_cfd) => {
todo!("but what?") todo!("but what?")
// Assumption: The maker publishes the CFD on chain
} }
} }
} }

4
daemon/src/to_sse_event.rs

@ -60,7 +60,7 @@ impl ToSseEvent for Vec<model::cfd::Cfd> {
.map(|cfd| { .map(|cfd| {
// TODO: Get the actual current price here // TODO: Get the actual current price here
let current_price = Usd::ZERO; let current_price = Usd::ZERO;
let (profit_btc, profit_usd) = cfd.calc_profit(current_price).unwrap(); let (profit_btc, profit_usd) = cfd.profit(current_price).unwrap();
Cfd { Cfd {
order_id: cfd.order.id, order_id: cfd.order.id,
@ -82,7 +82,7 @@ impl ToSseEvent for Vec<model::cfd::Cfd> {
// TODO: Depending on the state the margin might be set (i.e. in Open we save it // TODO: Depending on the state the margin might be set (i.e. in Open we save it
// in the DB internally) and does not have to be calculated // in the DB internally) and does not have to be calculated
margin: cfd.calc_margin().unwrap(), margin: cfd.margin().unwrap(),
} }
}) })
.collect::<Vec<Cfd>>(); .collect::<Vec<Cfd>>();

Loading…
Cancel
Save