Browse Source

Merge #252

252: Rework oracle actor communication r=da-kami a=da-kami



Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Daniel Karzel <daniel@comit.network>
compile-for-aarch64
bors[bot] 3 years ago
committed by GitHub
parent
commit
1280fe7132
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      cfd_protocol/tests/cfds.rs
  2. 65
      daemon/src/maker_cfd.rs
  3. 27
      daemon/src/model.rs
  4. 21
      daemon/src/model/cfd.rs
  5. 117
      daemon/src/oracle.rs
  6. 49
      daemon/src/taker_cfd.rs
  7. 7
      daemon/src/to_sse_event.rs

9
cfd_protocol/tests/cfds.rs

@ -1047,7 +1047,10 @@ impl OliviaData {
/// CFD protocol.
///
/// Data comes from this event:
/// https://outcome.observer/h00.ooo/x/BitMEX/BXBT/2021-10-05T02:00:00.price[n:20].
/// https://outcome.observer/h00.ooo/x/BitMEX/BXBT/2021-10-05T02:00:00.price?n=20.
///
/// Note: Due to removal of historic data these events cannot actually be retrieved from the
/// Oracle anymore.
fn example(id: &str, price: u64, nonce_pks: &[&str], attestations: &[&str]) -> Self {
let oracle_pk = schnorrsig::PublicKey::from_str(Self::OLIVIA_PK).unwrap();
@ -1082,7 +1085,7 @@ impl OliviaData {
const OLIVIA_PK: &'static str =
"ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7";
const EVENT_ID_0: &'static str = "/x/BitMEX/BXBT/2021-10-05T02:00:00.price[n:20]";
const EVENT_ID_0: &'static str = "/x/BitMEX/BXBT/2021-10-05T02:00:00.price?n=20";
const NONCE_PKS_0: [&'static str; 20] = [
"d02d163cf9623f567c4e3faf851a9266ac1ede13da4ca4141f3a7717fba9a739",
"bc310f26aa5addbc382f653d8530aaead7c25e3546abc24639f490e36d4bdb88",
@ -1129,7 +1132,7 @@ impl OliviaData {
"90c4d8ec9f408ccb62a62daa993c20f2f86799e1fdea520c6d060418e55fd216",
];
const EVENT_ID_1: &'static str = "/x/BitMEX/BXBT/2021-10-05T08:00:00.price[n:20]";
const EVENT_ID_1: &'static str = "/x/BitMEX/BXBT/2021-10-05T08:00:00.price?n=20";
const NONCE_PKS_1: [&'static str; 20] = [
"150df2e64f39706e726eaa1fe081af3edf376d9644723e135a99328fd194caca",
"b90629cedc7cb8430b4d15c84bbe1fe173e70e626d40c465e64de29d4879e20f",

65
daemon/src/maker_cfd.rs

@ -8,7 +8,7 @@ use crate::model::cfd::{
Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role,
RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals,
};
use crate::model::{OracleEventId, TakerId, Usd};
use crate::model::{TakerId, Usd};
use crate::monitor::MonitorParams;
use crate::wallet::Wallet;
use crate::{maker_inc_connections, monitor, oracle, setup_contract, wire};
@ -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::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::time::SystemTime;
use tokio::sync::watch;
use xtra::prelude::*;
@ -88,7 +88,6 @@ pub struct Actor {
monitor_actor: Address<monitor::Actor<Actor>>,
setup_state: SetupState,
roll_over_state: RollOverState,
latest_announcements: Option<BTreeMap<OracleEventId, oracle::Announcement>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
// Maker needs to also store TakerId to be able to send a reply back
current_pending_proposals: HashMap<OrderId, (UpdateCfdProposal, TakerId)>,
@ -135,7 +134,6 @@ impl Actor {
monitor_actor,
setup_state: SetupState::None,
roll_over_state: RollOverState::None,
latest_announcements: None,
oracle_actor,
current_pending_proposals: HashMap::new(),
}
@ -176,15 +174,8 @@ impl Actor {
min_quantity: Usd,
max_quantity: Usd,
) -> Result<()> {
let oracle_event_id = self
.latest_announcements
.clone()
.context("Cannot create order because no announcement from oracle")?
.iter()
.next_back()
.context("Empty list of announcements")?
.0
.clone();
let oracle_event_id =
oracle::next_announcement_after(time::OffsetDateTime::now_utc() + Order::TERM);
let order = Order::new(
price,
@ -520,13 +511,11 @@ impl Actor {
self.cfd_feed_actor_inbox
.send(load_all_cfds(&mut conn).await?)?;
let offer_announcements = self
.latest_announcements
.clone()
.context("No oracle announcements available")?;
let offer_announcement = offer_announcements
.get(&cfd.order.oracle_event_id)
.context("Order's announcement not found in current oracle announcements")?;
let offer_announcement = self
.oracle_actor
.send(oracle::GetAnnouncement(cfd.order.oracle_event_id.clone()))
.await?
.with_context(|| format!("Announcement {} not found", cfd.order.oracle_event_id))?;
self.oracle_actor
.do_send_async(oracle::MonitorEvent {
@ -701,13 +690,13 @@ impl Actor {
// TODO: do we want to store in the db that we rolled over?
let (oracle_event_id, announcement) = self
.latest_announcements
.clone()
.context("Cannot roll over because no announcement from oracle was found")?
.into_iter()
.next_back()
.context("Empty list of announcements")?;
let oracle_event_id =
oracle::next_announcement_after(time::OffsetDateTime::now_utc() + Order::TERM);
let announcement = self
.oracle_actor
.send(oracle::GetAnnouncement(oracle_event_id.clone()))
.await?
.with_context(|| format!("Announcement {} not found", oracle_event_id))?;
self.takers
.send(maker_inc_connections::TakerMessage {
@ -817,21 +806,6 @@ impl Actor {
Ok(())
}
async fn handle_oracle_announcements(
&mut self,
announcements: oracle::Announcements,
) -> Result<()> {
self.latest_announcements.replace(
announcements
.0
.iter()
.map(|announcement| (announcement.id.clone(), announcement.clone()))
.collect(),
);
Ok(())
}
async fn handle_oracle_attestation(&mut self, attestation: oracle::Attestation) -> Result<()> {
tracing::debug!(
"Learnt latest oracle attestation for event: {}",
@ -1021,13 +995,6 @@ impl Handler<TakerStreamMessage> for Actor {
}
}
#[async_trait]
impl Handler<oracle::Announcements> for Actor {
async fn handle(&mut self, msg: oracle::Announcements, _ctx: &mut Context<Self>) {
log_error!(self.handle_oracle_announcements(msg))
}
}
#[async_trait]
impl Handler<oracle::Attestation> for Actor {
async fn handle(&mut self, msg: oracle::Attestation, _ctx: &mut Context<Self>) {

27
daemon/src/model.rs

@ -6,7 +6,9 @@ use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use bdk::bitcoin::{Address, Amount};
use reqwest::Url;
use std::fmt;
use std::str::FromStr;
use std::time::SystemTime;
use uuid::Uuid;
@ -110,8 +112,33 @@ pub struct WalletInfo {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct OracleEventId(pub String);
impl OracleEventId {
pub fn to_olivia_url(&self) -> Url {
Url::from_str("https://h00.ooo")
.expect("valid URL from constant")
.join(self.0.as_str())
.expect("Event id can be joined")
}
}
impl Display for OracleEventId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_olivia_url() {
let url = OracleEventId("/x/BitMEX/BXBT/2021-09-23T10:00:00.price?n=20".to_string())
.to_olivia_url();
assert_eq!(
url,
Url::from_str("https://h00.ooo/x/BitMEX/BXBT/2021-09-23T10:00:00.price?n=20").unwrap()
);
}
}

21
daemon/src/model/cfd.rs

@ -15,7 +15,8 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::ops::{Neg, RangeInclusive};
use std::time::{Duration, SystemTime};
use std::time::SystemTime;
use time::Duration;
use uuid::Uuid;
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
@ -101,6 +102,8 @@ pub struct Order {
#[allow(dead_code)] // Only one binary and the tests use this.
impl Order {
pub const TERM: Duration = Duration::hours(24);
pub fn new(
price: Usd,
min_quantity: Usd,
@ -123,7 +126,7 @@ impl Order {
liquidation_price,
position: Position::Sell,
creation_timestamp: SystemTime::now(),
term: Duration::from_secs(60 * 60 * 8), // 8 hours
term: Self::TERM,
origin,
oracle_event_id,
})
@ -482,11 +485,7 @@ 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
(self.order.term * Cfd::REFUND_THRESHOLD).as_blocks().ceil() as u32
}
/// A factor to be added to the CFD order term for calculating the refund timelock.
@ -1031,7 +1030,7 @@ pub trait AsBlocks {
impl AsBlocks for Duration {
fn as_blocks(&self) -> f32 {
self.as_secs_f32() / 60.0 / 10.0
self.as_seconds_f32() / 60.0 / 10.0
}
}
@ -1140,15 +1139,15 @@ mod tests {
fn test_secs_into_blocks() {
let error_margin = f32::EPSILON;
let duration = Duration::from_secs(600);
let duration = Duration::seconds(600);
let blocks = duration.as_blocks();
assert!(blocks - error_margin < 1.0 && blocks + error_margin > 1.0);
let duration = Duration::from_secs(0);
let duration = Duration::seconds(0);
let blocks = duration.as_blocks();
assert!(blocks - error_margin < 0.0 && blocks + error_margin > 0.0);
let duration = Duration::from_secs(60);
let duration = Duration::seconds(60);
let blocks = duration.as_blocks();
assert!(blocks - error_margin < 0.1 && blocks + error_margin > 0.1);
}

117
daemon/src/oracle.rs

@ -11,21 +11,17 @@ use rocket::time::format_description::FormatItem;
use rocket::time::macros::format_description;
use rocket::time::{Duration, OffsetDateTime, Time};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::convert::TryFrom;
/// Where `olivia` is located.
const OLIVIA_URL: &str = "https://h00.ooo";
use std::collections::{HashMap, HashSet};
const OLIVIA_EVENT_TIME_FORMAT: &[FormatItem] =
format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
pub struct Actor<CFD, M>
where
CFD: xtra::Handler<Announcements> + xtra::Handler<Attestation>,
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
latest_announcements: Option<[Announcement; 24]>,
latest_announcements: HashMap<OracleEventId, Announcement>,
pending_attestations: HashSet<OracleEventId>,
cfd_actor_address: xtra::Address<CFD>,
monitor_actor_address: xtra::Address<M>,
@ -33,7 +29,7 @@ where
impl<CFD, M> Actor<CFD, M>
where
CFD: xtra::Handler<Announcements> + xtra::Handler<Attestation>,
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
pub fn new(
@ -69,7 +65,7 @@ where
}
Self {
latest_announcements: None,
latest_announcements: HashMap::new(),
pending_attestations,
cfd_actor_address,
monitor_actor_address,
@ -88,36 +84,29 @@ where
}
async fn update_latest_announcements(&mut self) -> Result<()> {
let new_announcements = next_urls()
let new_announcements = next_ids()
.into_iter()
.map(|event_url| async move {
let response = reqwest::get(event_url.clone())
.map(|event_id| async move {
let url = event_id.to_olivia_url();
let response = reqwest::get(url.clone())
.await
.with_context(|| format!("Failed to GET {}", event_url))?;
.with_context(|| format!("Failed to GET {}", url))?;
if !response.status().is_success() {
anyhow::bail!("GET {} responded with {}", event_url, response.status());
anyhow::bail!("GET {} responded with {}", url, response.status());
}
let announcement = response
.json::<Announcement>()
.await
.context("Failed to deserialize as Announcement")?;
Result::<_, anyhow::Error>::Ok(announcement)
Result::<_, anyhow::Error>::Ok((event_id, announcement))
})
.collect::<FuturesOrdered<_>>()
.try_collect::<Vec<_>>()
.try_collect::<HashMap<OracleEventId, Announcement>>()
.await?;
let new_announcements = <[Announcement; 24]>::try_from(new_announcements)
.map_err(|vec| anyhow::anyhow!("wrong number of announcements: {}", vec.len()))?;
if self.latest_announcements.as_ref() != Some(&new_announcements) {
self.latest_announcements = Some(new_announcements.clone());
self.cfd_actor_address
.do_send_async(Announcements(new_announcements))
.await?;
}
self.latest_announcements = new_announcements;
Ok(())
}
@ -126,7 +115,7 @@ where
let pending_attestations = self.pending_attestations.clone();
for event_id in pending_attestations.into_iter() {
{
let res = match reqwest::get(format!("{}{}", OLIVIA_URL, event_id)).await {
let res = match reqwest::get(event_id.to_olivia_url()).await {
Ok(res) if res.status().is_success() => res,
Ok(res) if res.status() == StatusCode::NOT_FOUND => {
tracing::trace!("Attestation not ready yet");
@ -165,11 +154,15 @@ where
tracing::trace!("Event {} already being monitored", event_id);
}
}
pub fn handle_get_announcement(&self, event_id: OracleEventId) -> Option<Announcement> {
self.latest_announcements.get(&event_id).cloned()
}
}
impl<CFD, M> xtra::Actor for Actor<CFD, M>
where
CFD: xtra::Handler<Announcements> + xtra::Handler<Attestation>,
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
}
@ -183,7 +176,7 @@ impl xtra::Message for Sync {
#[async_trait]
impl<CFD, M> xtra::Handler<Sync> for Actor<CFD, M>
where
CFD: xtra::Handler<Announcements> + xtra::Handler<Attestation>,
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
async fn handle(&mut self, _: Sync, _ctx: &mut xtra::Context<Self>) {
@ -202,7 +195,7 @@ impl xtra::Message for MonitorEvent {
#[async_trait]
impl<CFD, M> xtra::Handler<MonitorEvent> for Actor<CFD, M>
where
CFD: xtra::Handler<Announcements> + xtra::Handler<Attestation>,
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
async fn handle(&mut self, msg: MonitorEvent, _ctx: &mut xtra::Context<Self>) {
@ -210,12 +203,30 @@ where
}
}
#[derive(Debug, Clone)]
pub struct GetAnnouncement(pub OracleEventId);
#[async_trait]
impl<CFD, M> xtra::Handler<GetAnnouncement> for Actor<CFD, M>
where
CFD: xtra::Handler<Attestation>,
M: xtra::Handler<Attestation>,
{
async fn handle(
&mut self,
msg: GetAnnouncement,
_ctx: &mut xtra::Context<Self>,
) -> Option<Announcement> {
self.handle_get_announcement(msg.0)
}
}
/// Construct the URL of the next 24 `BitMEX/BXBT` hourly events
/// `olivia` will attest to.
fn next_urls() -> Vec<String> {
fn next_ids() -> Vec<OracleEventId> {
next_24_hours(OffsetDateTime::now_utc())
.into_iter()
.map(event_url)
.map(event_id)
.collect()
}
@ -224,14 +235,23 @@ fn next_24_hours(datetime: OffsetDateTime) -> Vec<OffsetDateTime> {
(1..=24).map(|i| adjusted + Duration::hours(i)).collect()
}
#[allow(dead_code)]
pub fn next_announcement_after(timestamp: OffsetDateTime) -> OracleEventId {
// always ceil to next hour
let adjusted =
timestamp.replace_time(Time::from_hms(timestamp.hour() + 1, 0, 0).expect("in_range"));
event_id(adjusted)
}
/// Construct the URL of `olivia`'s `BitMEX/BXBT` event to be attested
/// for at the time indicated by the argument `datetime`.
fn event_url(datetime: OffsetDateTime) -> String {
fn event_id(datetime: OffsetDateTime) -> OracleEventId {
let datetime = datetime
.format(&OLIVIA_EVENT_TIME_FORMAT)
.expect("valid formatter for datetime");
format!("{}/x/BitMEX/BXBT/{}.price[n:20]", OLIVIA_URL, datetime)
OracleEventId(format!("/x/BitMEX/BXBT/{}.price?n=20", datetime))
}
#[derive(Debug, Clone, serde::Deserialize, PartialEq)]
@ -255,9 +275,6 @@ impl From<Announcement> for cfd_protocol::Announcement {
}
}
#[derive(Debug, Clone)]
pub struct Announcements(pub [Announcement; 24]);
// TODO: Implement real deserialization once price attestation is
// implemented in `olivia`
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@ -268,8 +285,8 @@ pub struct Attestation {
pub scalars: Vec<SecretKey>,
}
impl xtra::Message for Announcements {
type Result = ();
impl xtra::Message for GetAnnouncement {
type Result = Option<Announcement>;
}
impl xtra::Message for Attestation {
@ -394,11 +411,11 @@ mod olivia_api {
#[test]
fn deserialize_announcement() {
let json = r#"{"announcement":{"oracle_event":{"encoding":"json","data":"{\"id\":\"/x/BitMEX/BXBT/2021-10-04T22:00:00.price[n:20]\",\"expected-outcome-time\":\"2021-10-04T22:00:00\",\"descriptor\":{\"type\":\"digit-decomposition\",\"is_signed\":false,\"n_digits\":20,\"unit\":null},\"schemes\":{\"olivia-v1\":{\"nonces\":[\"8d72028eeaf4b85aec0f750f05a4a320cac193f5d8494bfe05cd4b29f3df4239\",\"77240f79a0042adae35ad24284b18b906f17a979fcec3c90d11ed682c6b9261e\",\"e42332407b58f7c6e860b886acfe8d19636fb21a1e20722522206b30a2424d89\",\"ce1158e02dc265751887edae9bdcf8d06ad40489c7643324ccb6a46e4e740f5a\",\"52a5751a43046217bcf009df917c24e400c6da645474a654a5f89499df7154d4\",\"e7b97360a952c2b239d1bfeaade73da4a38e83d20f5deb5b054bcbbc78c91e40\",\"612ce13fd61be10e8de77976c6d479865bc3d2ebdc212946f1e5d93e3f504d2e\",\"e40decd0ea27003b873dde9b6be02f1b344e7e74bc5299144fa0f37b1cf12e90\",\"281a829e05d5f8b96eaf620c7b26115bfb29013d503b6bb40068cdb413a87197\",\"3c87eed0a3852953b0f3ac8a47ff194de66c7229c42e6578e0f6464ba240f033\",\"29028525277cb39adab9ac145d6ce61f2e10306e7b6ce95970a22ea3b201a5d9\",\"20971b4d2069d8b9b5c5678290ab7624821cf32ffe32a20d58428ca90da02523\",\"667a9af33ed45bfb5c4fc7adacea15bbe26df90e0df7dd5b8235e14dfd0da38f\",\"224df2d2706b5c629173b84927e2b206dad7a72e132eb86912d9464dad4b41d1\",\"85296962b9d1f7699c248467ce94ce4aa6e00d26fe01af3a507bcd3a303855d4\",\"96813c9f4d136f0f64be79e73d657fecc43d8b6c463163913b4fa31f96b1ae6b\",\"9d5971aa596923560b12f367fb2f4e192d8906bf6ed3a58b093f50d3cad27493\",\"b7f2c135db80cee02b4436557c78dc1dd2343c1a3688ba736c6c40e9531547b6\",\"bd6236fc18f1dc96f9755cc5c435adaf3952ff810d3ad5b96a03464a61eecfde\",\"20b2922ce326e5e2f4ed683723a879e467edd1068bf5a3c4f331525216227abe\"]},\"ecdsa-v1\":{}}}"},"signature":"743ed9900aba5a1ba3ba9d862628cdc5cca27974c40c4ab64618709021b3fbb13216a3efc733be260025da487ae9b63a8290d555bdc8da6324deff149fc7b110"},"attestation":{"outcome":"48935","schemes":{"olivia-v1":{"scalars":["1327b3bd0f1faf45d6fed6c96d0c158da22a2033a6fed98bed036df0a4eef484","72659c6beebd45e299bc4260a1c1ffd708ed33771459563502f25fc4f537cef6","051eec45417e2493f36b13f4fdf83fb981be42901bf876e4ac594ff2daa4c30e","847d8c7204335b1dbc2078cfb56118b1977162e7b997f2029f490929bbd603c7","5b695846292b6d69d9beedcc7dd2b7e49fd49ec4fcf262d9357f52b049fa8998","368a1f2206fcedcde37381b272fa5a400f55ef720ee2b8fff558e3b0dce729ee","9e1c015c0e827037f18681937764f4973ef22d6fbbd82f6bde3bf5198f6b8999","fe9620c9ad9862b5615f8cf3e20e8d9f422e7410914ce8af2b8bad8937b75738","44297ae831898f8f5c7e57720f233a717e9034a5b41d6c89cce6d9058c4ee086","587fc9b71f1920df825138f00bc625e6610e61b1fec0a64e2800fc05b3a2e96d","010377f6b885ae48d62e7863c8038240aafe0a7fb97d58ac6173186c95335955","5243782226739f59b0ac01a56a63537289ffe81b87b33eca42f89f7848623520","06184cb8e46b5d520cd9b5829feeb73b688d61e5f37b91ff88d3f9b8664a5cdd","fe48f4b568bb501732c4e8f1919940c9bca0ad909f4624658b14664af823ccfe","0841f121e7a54f88a844227cd0ae62171b49d004120c16d1a1d619f0b76f7068","c4ac3c8751a63f7c40062b9b84f2bb953b0e6bd8f2cf3b2bcaf711321e92df8f","86a2b1a31bf80f17c00ab28420c636c1ed604d0b1f0a33adda99a0cf1e510269","fb892eba992b723a06bccad6a2a1bb875d548a275a987266fceed097b9fd88db","41991fb15fdb013ccab3e6674b91546a0e1e56a1e212c8795c76d0b43f4c884d","ab6a4368d2e5e7cea23fd648662769facc1c37f1d1613225e9010af07cd74711"]},"ecdsa-v1":{"signature":"1d9a5e2336883cc6b440ff40e16ee44f8af2ba9313e46f1e4cd417f7dba7686279b0216e4b0b5fcf0c650dbad98fdefcf5ef16b49d63651a87f80caddd472384"}},"time":"2021-10-04T22:00:15"}}"#;
let json = r#"{"announcement":{"oracle_event":{"encoding":"json","data":"{\"id\":\"/x/BitMEX/BXBT/2021-10-04T22:00:00.price?n=20\",\"expected-outcome-time\":\"2021-10-04T22:00:00\",\"descriptor\":{\"type\":\"digit-decomposition\",\"is_signed\":false,\"n_digits\":20,\"unit\":null},\"schemes\":{\"olivia-v1\":{\"nonces\":[\"8d72028eeaf4b85aec0f750f05a4a320cac193f5d8494bfe05cd4b29f3df4239\",\"77240f79a0042adae35ad24284b18b906f17a979fcec3c90d11ed682c6b9261e\",\"e42332407b58f7c6e860b886acfe8d19636fb21a1e20722522206b30a2424d89\",\"ce1158e02dc265751887edae9bdcf8d06ad40489c7643324ccb6a46e4e740f5a\",\"52a5751a43046217bcf009df917c24e400c6da645474a654a5f89499df7154d4\",\"e7b97360a952c2b239d1bfeaade73da4a38e83d20f5deb5b054bcbbc78c91e40\",\"612ce13fd61be10e8de77976c6d479865bc3d2ebdc212946f1e5d93e3f504d2e\",\"e40decd0ea27003b873dde9b6be02f1b344e7e74bc5299144fa0f37b1cf12e90\",\"281a829e05d5f8b96eaf620c7b26115bfb29013d503b6bb40068cdb413a87197\",\"3c87eed0a3852953b0f3ac8a47ff194de66c7229c42e6578e0f6464ba240f033\",\"29028525277cb39adab9ac145d6ce61f2e10306e7b6ce95970a22ea3b201a5d9\",\"20971b4d2069d8b9b5c5678290ab7624821cf32ffe32a20d58428ca90da02523\",\"667a9af33ed45bfb5c4fc7adacea15bbe26df90e0df7dd5b8235e14dfd0da38f\",\"224df2d2706b5c629173b84927e2b206dad7a72e132eb86912d9464dad4b41d1\",\"85296962b9d1f7699c248467ce94ce4aa6e00d26fe01af3a507bcd3a303855d4\",\"96813c9f4d136f0f64be79e73d657fecc43d8b6c463163913b4fa31f96b1ae6b\",\"9d5971aa596923560b12f367fb2f4e192d8906bf6ed3a58b093f50d3cad27493\",\"b7f2c135db80cee02b4436557c78dc1dd2343c1a3688ba736c6c40e9531547b6\",\"bd6236fc18f1dc96f9755cc5c435adaf3952ff810d3ad5b96a03464a61eecfde\",\"20b2922ce326e5e2f4ed683723a879e467edd1068bf5a3c4f331525216227abe\"]},\"ecdsa-v1\":{}}}"},"signature":"743ed9900aba5a1ba3ba9d862628cdc5cca27974c40c4ab64618709021b3fbb13216a3efc733be260025da487ae9b63a8290d555bdc8da6324deff149fc7b110"},"attestation":{"outcome":"48935","schemes":{"olivia-v1":{"scalars":["1327b3bd0f1faf45d6fed6c96d0c158da22a2033a6fed98bed036df0a4eef484","72659c6beebd45e299bc4260a1c1ffd708ed33771459563502f25fc4f537cef6","051eec45417e2493f36b13f4fdf83fb981be42901bf876e4ac594ff2daa4c30e","847d8c7204335b1dbc2078cfb56118b1977162e7b997f2029f490929bbd603c7","5b695846292b6d69d9beedcc7dd2b7e49fd49ec4fcf262d9357f52b049fa8998","368a1f2206fcedcde37381b272fa5a400f55ef720ee2b8fff558e3b0dce729ee","9e1c015c0e827037f18681937764f4973ef22d6fbbd82f6bde3bf5198f6b8999","fe9620c9ad9862b5615f8cf3e20e8d9f422e7410914ce8af2b8bad8937b75738","44297ae831898f8f5c7e57720f233a717e9034a5b41d6c89cce6d9058c4ee086","587fc9b71f1920df825138f00bc625e6610e61b1fec0a64e2800fc05b3a2e96d","010377f6b885ae48d62e7863c8038240aafe0a7fb97d58ac6173186c95335955","5243782226739f59b0ac01a56a63537289ffe81b87b33eca42f89f7848623520","06184cb8e46b5d520cd9b5829feeb73b688d61e5f37b91ff88d3f9b8664a5cdd","fe48f4b568bb501732c4e8f1919940c9bca0ad909f4624658b14664af823ccfe","0841f121e7a54f88a844227cd0ae62171b49d004120c16d1a1d619f0b76f7068","c4ac3c8751a63f7c40062b9b84f2bb953b0e6bd8f2cf3b2bcaf711321e92df8f","86a2b1a31bf80f17c00ab28420c636c1ed604d0b1f0a33adda99a0cf1e510269","fb892eba992b723a06bccad6a2a1bb875d548a275a987266fceed097b9fd88db","41991fb15fdb013ccab3e6674b91546a0e1e56a1e212c8795c76d0b43f4c884d","ab6a4368d2e5e7cea23fd648662769facc1c37f1d1613225e9010af07cd74711"]},"ecdsa-v1":{"signature":"1d9a5e2336883cc6b440ff40e16ee44f8af2ba9313e46f1e4cd417f7dba7686279b0216e4b0b5fcf0c650dbad98fdefcf5ef16b49d63651a87f80caddd472384"}},"time":"2021-10-04T22:00:15"}}"#;
let deserialized = serde_json::from_str::<oracle::Announcement>(json).unwrap();
let expected = oracle::Announcement {
id: OracleEventId("/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"
@ -469,11 +486,11 @@ mod olivia_api {
#[test]
fn deserialize_attestation() {
let json = r#"{"announcement":{"oracle_event":{"encoding":"json","data":"{\"id\":\"/x/BitMEX/BXBT/2021-10-04T22:00:00.price[n:20]\",\"expected-outcome-time\":\"2021-10-04T22:00:00\",\"descriptor\":{\"type\":\"digit-decomposition\",\"is_signed\":false,\"n_digits\":20,\"unit\":null},\"schemes\":{\"olivia-v1\":{\"nonces\":[\"8d72028eeaf4b85aec0f750f05a4a320cac193f5d8494bfe05cd4b29f3df4239\",\"77240f79a0042adae35ad24284b18b906f17a979fcec3c90d11ed682c6b9261e\",\"e42332407b58f7c6e860b886acfe8d19636fb21a1e20722522206b30a2424d89\",\"ce1158e02dc265751887edae9bdcf8d06ad40489c7643324ccb6a46e4e740f5a\",\"52a5751a43046217bcf009df917c24e400c6da645474a654a5f89499df7154d4\",\"e7b97360a952c2b239d1bfeaade73da4a38e83d20f5deb5b054bcbbc78c91e40\",\"612ce13fd61be10e8de77976c6d479865bc3d2ebdc212946f1e5d93e3f504d2e\",\"e40decd0ea27003b873dde9b6be02f1b344e7e74bc5299144fa0f37b1cf12e90\",\"281a829e05d5f8b96eaf620c7b26115bfb29013d503b6bb40068cdb413a87197\",\"3c87eed0a3852953b0f3ac8a47ff194de66c7229c42e6578e0f6464ba240f033\",\"29028525277cb39adab9ac145d6ce61f2e10306e7b6ce95970a22ea3b201a5d9\",\"20971b4d2069d8b9b5c5678290ab7624821cf32ffe32a20d58428ca90da02523\",\"667a9af33ed45bfb5c4fc7adacea15bbe26df90e0df7dd5b8235e14dfd0da38f\",\"224df2d2706b5c629173b84927e2b206dad7a72e132eb86912d9464dad4b41d1\",\"85296962b9d1f7699c248467ce94ce4aa6e00d26fe01af3a507bcd3a303855d4\",\"96813c9f4d136f0f64be79e73d657fecc43d8b6c463163913b4fa31f96b1ae6b\",\"9d5971aa596923560b12f367fb2f4e192d8906bf6ed3a58b093f50d3cad27493\",\"b7f2c135db80cee02b4436557c78dc1dd2343c1a3688ba736c6c40e9531547b6\",\"bd6236fc18f1dc96f9755cc5c435adaf3952ff810d3ad5b96a03464a61eecfde\",\"20b2922ce326e5e2f4ed683723a879e467edd1068bf5a3c4f331525216227abe\"]},\"ecdsa-v1\":{}}}"},"signature":"743ed9900aba5a1ba3ba9d862628cdc5cca27974c40c4ab64618709021b3fbb13216a3efc733be260025da487ae9b63a8290d555bdc8da6324deff149fc7b110"},"attestation":{"outcome":"48935","schemes":{"olivia-v1":{"scalars":["1327b3bd0f1faf45d6fed6c96d0c158da22a2033a6fed98bed036df0a4eef484","72659c6beebd45e299bc4260a1c1ffd708ed33771459563502f25fc4f537cef6","051eec45417e2493f36b13f4fdf83fb981be42901bf876e4ac594ff2daa4c30e","847d8c7204335b1dbc2078cfb56118b1977162e7b997f2029f490929bbd603c7","5b695846292b6d69d9beedcc7dd2b7e49fd49ec4fcf262d9357f52b049fa8998","368a1f2206fcedcde37381b272fa5a400f55ef720ee2b8fff558e3b0dce729ee","9e1c015c0e827037f18681937764f4973ef22d6fbbd82f6bde3bf5198f6b8999","fe9620c9ad9862b5615f8cf3e20e8d9f422e7410914ce8af2b8bad8937b75738","44297ae831898f8f5c7e57720f233a717e9034a5b41d6c89cce6d9058c4ee086","587fc9b71f1920df825138f00bc625e6610e61b1fec0a64e2800fc05b3a2e96d","010377f6b885ae48d62e7863c8038240aafe0a7fb97d58ac6173186c95335955","5243782226739f59b0ac01a56a63537289ffe81b87b33eca42f89f7848623520","06184cb8e46b5d520cd9b5829feeb73b688d61e5f37b91ff88d3f9b8664a5cdd","fe48f4b568bb501732c4e8f1919940c9bca0ad909f4624658b14664af823ccfe","0841f121e7a54f88a844227cd0ae62171b49d004120c16d1a1d619f0b76f7068","c4ac3c8751a63f7c40062b9b84f2bb953b0e6bd8f2cf3b2bcaf711321e92df8f","86a2b1a31bf80f17c00ab28420c636c1ed604d0b1f0a33adda99a0cf1e510269","fb892eba992b723a06bccad6a2a1bb875d548a275a987266fceed097b9fd88db","41991fb15fdb013ccab3e6674b91546a0e1e56a1e212c8795c76d0b43f4c884d","ab6a4368d2e5e7cea23fd648662769facc1c37f1d1613225e9010af07cd74711"]},"ecdsa-v1":{"signature":"1d9a5e2336883cc6b440ff40e16ee44f8af2ba9313e46f1e4cd417f7dba7686279b0216e4b0b5fcf0c650dbad98fdefcf5ef16b49d63651a87f80caddd472384"}},"time":"2021-10-04T22:00:15"}}"#;
let json = r#"{"announcement":{"oracle_event":{"encoding":"json","data":"{\"id\":\"/x/BitMEX/BXBT/2021-10-04T22:00:00.price?n=20\",\"expected-outcome-time\":\"2021-10-04T22:00:00\",\"descriptor\":{\"type\":\"digit-decomposition\",\"is_signed\":false,\"n_digits\":20,\"unit\":null},\"schemes\":{\"olivia-v1\":{\"nonces\":[\"8d72028eeaf4b85aec0f750f05a4a320cac193f5d8494bfe05cd4b29f3df4239\",\"77240f79a0042adae35ad24284b18b906f17a979fcec3c90d11ed682c6b9261e\",\"e42332407b58f7c6e860b886acfe8d19636fb21a1e20722522206b30a2424d89\",\"ce1158e02dc265751887edae9bdcf8d06ad40489c7643324ccb6a46e4e740f5a\",\"52a5751a43046217bcf009df917c24e400c6da645474a654a5f89499df7154d4\",\"e7b97360a952c2b239d1bfeaade73da4a38e83d20f5deb5b054bcbbc78c91e40\",\"612ce13fd61be10e8de77976c6d479865bc3d2ebdc212946f1e5d93e3f504d2e\",\"e40decd0ea27003b873dde9b6be02f1b344e7e74bc5299144fa0f37b1cf12e90\",\"281a829e05d5f8b96eaf620c7b26115bfb29013d503b6bb40068cdb413a87197\",\"3c87eed0a3852953b0f3ac8a47ff194de66c7229c42e6578e0f6464ba240f033\",\"29028525277cb39adab9ac145d6ce61f2e10306e7b6ce95970a22ea3b201a5d9\",\"20971b4d2069d8b9b5c5678290ab7624821cf32ffe32a20d58428ca90da02523\",\"667a9af33ed45bfb5c4fc7adacea15bbe26df90e0df7dd5b8235e14dfd0da38f\",\"224df2d2706b5c629173b84927e2b206dad7a72e132eb86912d9464dad4b41d1\",\"85296962b9d1f7699c248467ce94ce4aa6e00d26fe01af3a507bcd3a303855d4\",\"96813c9f4d136f0f64be79e73d657fecc43d8b6c463163913b4fa31f96b1ae6b\",\"9d5971aa596923560b12f367fb2f4e192d8906bf6ed3a58b093f50d3cad27493\",\"b7f2c135db80cee02b4436557c78dc1dd2343c1a3688ba736c6c40e9531547b6\",\"bd6236fc18f1dc96f9755cc5c435adaf3952ff810d3ad5b96a03464a61eecfde\",\"20b2922ce326e5e2f4ed683723a879e467edd1068bf5a3c4f331525216227abe\"]},\"ecdsa-v1\":{}}}"},"signature":"743ed9900aba5a1ba3ba9d862628cdc5cca27974c40c4ab64618709021b3fbb13216a3efc733be260025da487ae9b63a8290d555bdc8da6324deff149fc7b110"},"attestation":{"outcome":"48935","schemes":{"olivia-v1":{"scalars":["1327b3bd0f1faf45d6fed6c96d0c158da22a2033a6fed98bed036df0a4eef484","72659c6beebd45e299bc4260a1c1ffd708ed33771459563502f25fc4f537cef6","051eec45417e2493f36b13f4fdf83fb981be42901bf876e4ac594ff2daa4c30e","847d8c7204335b1dbc2078cfb56118b1977162e7b997f2029f490929bbd603c7","5b695846292b6d69d9beedcc7dd2b7e49fd49ec4fcf262d9357f52b049fa8998","368a1f2206fcedcde37381b272fa5a400f55ef720ee2b8fff558e3b0dce729ee","9e1c015c0e827037f18681937764f4973ef22d6fbbd82f6bde3bf5198f6b8999","fe9620c9ad9862b5615f8cf3e20e8d9f422e7410914ce8af2b8bad8937b75738","44297ae831898f8f5c7e57720f233a717e9034a5b41d6c89cce6d9058c4ee086","587fc9b71f1920df825138f00bc625e6610e61b1fec0a64e2800fc05b3a2e96d","010377f6b885ae48d62e7863c8038240aafe0a7fb97d58ac6173186c95335955","5243782226739f59b0ac01a56a63537289ffe81b87b33eca42f89f7848623520","06184cb8e46b5d520cd9b5829feeb73b688d61e5f37b91ff88d3f9b8664a5cdd","fe48f4b568bb501732c4e8f1919940c9bca0ad909f4624658b14664af823ccfe","0841f121e7a54f88a844227cd0ae62171b49d004120c16d1a1d619f0b76f7068","c4ac3c8751a63f7c40062b9b84f2bb953b0e6bd8f2cf3b2bcaf711321e92df8f","86a2b1a31bf80f17c00ab28420c636c1ed604d0b1f0a33adda99a0cf1e510269","fb892eba992b723a06bccad6a2a1bb875d548a275a987266fceed097b9fd88db","41991fb15fdb013ccab3e6674b91546a0e1e56a1e212c8795c76d0b43f4c884d","ab6a4368d2e5e7cea23fd648662769facc1c37f1d1613225e9010af07cd74711"]},"ecdsa-v1":{"signature":"1d9a5e2336883cc6b440ff40e16ee44f8af2ba9313e46f1e4cd417f7dba7686279b0216e4b0b5fcf0c650dbad98fdefcf5ef16b49d63651a87f80caddd472384"}},"time":"2021-10-04T22:00:15"}}"#;
let deserialized = serde_json::from_str::<oracle::Attestation>(json).unwrap();
let expected = oracle::Attestation {
id: OracleEventId("/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()),
price: 48935,
scalars: vec![
"1327b3bd0f1faf45d6fed6c96d0c158da22a2033a6fed98bed036df0a4eef484"
@ -550,13 +567,17 @@ mod tests {
use time::macros::datetime;
#[test]
fn next_event_url_is_correct() {
let url = event_url(datetime!(2021-09-23 10:00:00).assume_utc());
fn next_event_id_is_correct() {
let event_id = event_id(datetime!(2021-09-23 10:00:00).assume_utc());
assert_eq!(event_id.0, "/x/BitMEX/BXBT/2021-09-23T10:00:00.price?n=20");
}
#[test]
fn next_event_id_after_timestamp() {
let event_id = next_announcement_after(datetime!(2021-09-23 10:40:00).assume_utc());
assert_eq!(
url,
"https://h00.ooo/x/BitMEX/BXBT/2021-09-23T10:00:00.price[n:20]"
);
assert_eq!(event_id.0, "/x/BitMEX/BXBT/2021-09-23T11:00:00.price?n=20");
}
#[test]

49
daemon/src/taker_cfd.rs

@ -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::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::time::SystemTime;
use tokio::sync::watch;
use xtra::prelude::*;
@ -80,7 +80,6 @@ pub struct Actor {
monitor_actor: Address<monitor::Actor<Actor>>,
setup_state: SetupState,
roll_over_state: RollOverState,
latest_announcements: Option<BTreeMap<OracleEventId, oracle::Announcement>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
current_pending_proposals: UpdateCfdProposals,
}
@ -110,7 +109,6 @@ impl Actor {
setup_state: SetupState::None,
roll_over_state: RollOverState::None,
oracle_actor,
latest_announcements: None,
current_pending_proposals: HashMap::new(),
}
}
@ -271,13 +269,11 @@ impl Actor {
.send(load_all_cfds(&mut conn).await?)?;
let cfd = load_cfd_by_order_id(order_id, &mut conn).await?;
let offer_announcements = self
.latest_announcements
.clone()
.context("No oracle announcements available")?;
let offer_announcement = offer_announcements
.get(&cfd.order.oracle_event_id)
.context("Order's announcement not found in current oracle announcements")?;
let offer_announcement = self
.oracle_actor
.send(oracle::GetAnnouncement(cfd.order.oracle_event_id.clone()))
.await?
.with_context(|| format!("Announcement {} not found", cfd.order.oracle_event_id))?;
self.oracle_actor
.do_send_async(oracle::MonitorEvent {
@ -367,14 +363,11 @@ impl Actor {
let cfd = load_cfd_by_order_id(order_id, &mut conn).await?;
let dlc = cfd.open_dlc().context("CFD was in wrong state")?;
// TODO: we need to get multiple announcements for the next 24h
let announcement = self
.latest_announcements
.clone()
.context("Cannot roll over because no announcement from oracle was found")?
.get(&oracle_event_id)
.context("Empty list of announcements")?
.clone();
.oracle_actor
.send(oracle::GetAnnouncement(oracle_event_id.clone()))
.await?
.with_context(|| format!("Announcement {} not found", oracle_event_id))?;
self.oracle_actor
.do_send_async(oracle::MonitorEvent {
@ -594,21 +587,6 @@ impl Actor {
Ok(())
}
async fn handle_oracle_announcements(
&mut self,
announcements: oracle::Announcements,
) -> Result<()> {
self.latest_announcements.replace(
announcements
.0
.iter()
.map(|announcement| (announcement.id.clone(), announcement.clone()))
.collect(),
);
Ok(())
}
async fn handle_oracle_attestation(&mut self, attestation: oracle::Attestation) -> Result<()> {
tracing::debug!(
"Learnt latest oracle attestation for event: {}",
@ -748,13 +726,6 @@ impl Handler<monitor::Event> for Actor {
}
}
#[async_trait]
impl Handler<oracle::Announcements> for Actor {
async fn handle(&mut self, msg: oracle::Announcements, _ctx: &mut Context<Self>) {
log_error!(self.handle_oracle_announcements(msg))
}
}
#[async_trait]
impl Handler<oracle::Attestation> for Actor {
async fn handle(&mut self, msg: oracle::Attestation, _ctx: &mut Context<Self>) {

7
daemon/src/to_sse_event.rs

@ -6,6 +6,7 @@ use rocket::request::FromParam;
use rocket::response::stream::Event;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::watch;
@ -200,7 +201,11 @@ impl ToSseEvent for Option<model::cfd::Order> {
.duration_since(UNIX_EPOCH)
.expect("timestamp to be convertible to duration since epoch")
.as_secs(),
term_in_secs: order.term.as_secs(),
term_in_secs: order
.term
.whole_seconds()
.try_into()
.expect("term is always positive number"),
});
Event::json(&order).event("order")

Loading…
Cancel
Save