| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- //! Types representing binary encoding of signable payloads and signature envelopes.
- use {
- super::router::{PriceFeedId, PriceFeedProperty},
- crate::{
- router::{ChannelId, Price, Rate},
- time::TimestampUs,
- },
- anyhow::bail,
- byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt, BE, LE},
- serde::{Deserialize, Serialize},
- std::{
- io::{Cursor, Read, Write},
- num::NonZeroI64,
- },
- };
- /// Data contained within a signable payload.
- #[derive(Debug, Clone, PartialEq, Eq, Hash)]
- pub struct PayloadData {
- pub timestamp_us: TimestampUs,
- pub channel_id: ChannelId,
- // TODO: smallvec?
- pub feeds: Vec<PayloadFeedData>,
- }
- #[derive(Debug, Clone, PartialEq, Eq, Hash)]
- pub struct PayloadFeedData {
- pub feed_id: PriceFeedId,
- // TODO: smallvec?
- pub properties: Vec<PayloadPropertyValue>,
- }
- #[derive(Debug, Clone, PartialEq, Eq, Hash)]
- pub enum PayloadPropertyValue {
- Price(Option<Price>),
- BestBidPrice(Option<Price>),
- BestAskPrice(Option<Price>),
- PublisherCount(u16),
- Exponent(i16),
- Confidence(Option<Price>),
- FundingRate(Option<Rate>),
- FundingTimestamp(Option<TimestampUs>),
- }
- #[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
- pub struct AggregatedPriceFeedData {
- pub price: Option<Price>,
- pub best_bid_price: Option<Price>,
- pub best_ask_price: Option<Price>,
- pub publisher_count: u16,
- pub confidence: Option<Price>,
- pub funding_rate: Option<Rate>,
- pub funding_timestamp: Option<TimestampUs>,
- }
- /// First bytes of a payload's encoding
- /// (in LE or BE depending on the byte order used for encoding the rest of the payload)
- pub const PAYLOAD_FORMAT_MAGIC: u32 = 2479346549;
- impl PayloadData {
- pub fn new(
- timestamp_us: TimestampUs,
- channel_id: ChannelId,
- feeds: &[(PriceFeedId, i16, AggregatedPriceFeedData)],
- requested_properties: &[PriceFeedProperty],
- ) -> Self {
- Self {
- timestamp_us,
- channel_id,
- feeds: feeds
- .iter()
- .map(|(feed_id, exponent, feed)| PayloadFeedData {
- feed_id: *feed_id,
- properties: requested_properties
- .iter()
- .map(|property| match property {
- PriceFeedProperty::Price => PayloadPropertyValue::Price(feed.price),
- PriceFeedProperty::BestBidPrice => {
- PayloadPropertyValue::BestBidPrice(feed.best_bid_price)
- }
- PriceFeedProperty::BestAskPrice => {
- PayloadPropertyValue::BestAskPrice(feed.best_ask_price)
- }
- PriceFeedProperty::PublisherCount => {
- PayloadPropertyValue::PublisherCount(feed.publisher_count)
- }
- PriceFeedProperty::Exponent => {
- PayloadPropertyValue::Exponent(*exponent)
- }
- PriceFeedProperty::Confidence => {
- PayloadPropertyValue::Confidence(feed.confidence)
- }
- PriceFeedProperty::FundingRate => {
- PayloadPropertyValue::FundingRate(feed.funding_rate)
- }
- PriceFeedProperty::FundingTimestamp => {
- PayloadPropertyValue::FundingTimestamp(feed.funding_timestamp)
- }
- })
- .collect(),
- })
- .collect(),
- }
- }
- pub fn serialize<BO: ByteOrder>(&self, mut writer: impl Write) -> anyhow::Result<()> {
- writer.write_u32::<BO>(PAYLOAD_FORMAT_MAGIC)?;
- writer.write_u64::<BO>(self.timestamp_us.as_micros())?;
- writer.write_u8(self.channel_id.0)?;
- writer.write_u8(self.feeds.len().try_into()?)?;
- for feed in &self.feeds {
- writer.write_u32::<BO>(feed.feed_id.0)?;
- writer.write_u8(feed.properties.len().try_into()?)?;
- for property in &feed.properties {
- match property {
- PayloadPropertyValue::Price(price) => {
- writer.write_u8(PriceFeedProperty::Price as u8)?;
- write_option_price::<BO>(&mut writer, *price)?;
- }
- PayloadPropertyValue::BestBidPrice(price) => {
- writer.write_u8(PriceFeedProperty::BestBidPrice as u8)?;
- write_option_price::<BO>(&mut writer, *price)?;
- }
- PayloadPropertyValue::BestAskPrice(price) => {
- writer.write_u8(PriceFeedProperty::BestAskPrice as u8)?;
- write_option_price::<BO>(&mut writer, *price)?;
- }
- PayloadPropertyValue::PublisherCount(count) => {
- writer.write_u8(PriceFeedProperty::PublisherCount as u8)?;
- writer.write_u16::<BO>(*count)?;
- }
- PayloadPropertyValue::Exponent(exponent) => {
- writer.write_u8(PriceFeedProperty::Exponent as u8)?;
- writer.write_i16::<BO>(*exponent)?;
- }
- PayloadPropertyValue::Confidence(confidence) => {
- writer.write_u8(PriceFeedProperty::Confidence as u8)?;
- write_option_price::<BO>(&mut writer, *confidence)?;
- }
- PayloadPropertyValue::FundingRate(rate) => {
- writer.write_u8(PriceFeedProperty::FundingRate as u8)?;
- write_option_rate::<BO>(&mut writer, *rate)?;
- }
- PayloadPropertyValue::FundingTimestamp(timestamp) => {
- writer.write_u8(PriceFeedProperty::FundingTimestamp as u8)?;
- write_option_timestamp::<BO>(&mut writer, *timestamp)?;
- }
- }
- }
- }
- Ok(())
- }
- pub fn deserialize_slice_le(data: &[u8]) -> anyhow::Result<Self> {
- Self::deserialize::<LE>(Cursor::new(data))
- }
- pub fn deserialize_slice_be(data: &[u8]) -> anyhow::Result<Self> {
- Self::deserialize::<BE>(Cursor::new(data))
- }
- pub fn deserialize<BO: ByteOrder>(mut reader: impl Read) -> anyhow::Result<Self> {
- let magic = reader.read_u32::<BO>()?;
- if magic != PAYLOAD_FORMAT_MAGIC {
- bail!("magic mismatch");
- }
- let timestamp_us = TimestampUs::from_micros(reader.read_u64::<BO>()?);
- let channel_id = ChannelId(reader.read_u8()?);
- let num_feeds = reader.read_u8()?;
- let mut feeds = Vec::with_capacity(num_feeds.into());
- for _ in 0..num_feeds {
- let feed_id = PriceFeedId(reader.read_u32::<BO>()?);
- let num_properties = reader.read_u8()?;
- let mut feed = PayloadFeedData {
- feed_id,
- properties: Vec::with_capacity(num_properties.into()),
- };
- for _ in 0..num_properties {
- let property = reader.read_u8()?;
- let value = if property == PriceFeedProperty::Price as u8 {
- PayloadPropertyValue::Price(read_option_price::<BO>(&mut reader)?)
- } else if property == PriceFeedProperty::BestBidPrice as u8 {
- PayloadPropertyValue::BestBidPrice(read_option_price::<BO>(&mut reader)?)
- } else if property == PriceFeedProperty::BestAskPrice as u8 {
- PayloadPropertyValue::BestAskPrice(read_option_price::<BO>(&mut reader)?)
- } else if property == PriceFeedProperty::PublisherCount as u8 {
- PayloadPropertyValue::PublisherCount(reader.read_u16::<BO>()?)
- } else if property == PriceFeedProperty::Exponent as u8 {
- PayloadPropertyValue::Exponent(reader.read_i16::<BO>()?)
- } else if property == PriceFeedProperty::Confidence as u8 {
- PayloadPropertyValue::Confidence(read_option_price::<BO>(&mut reader)?)
- } else if property == PriceFeedProperty::FundingRate as u8 {
- PayloadPropertyValue::FundingRate(read_option_rate::<BO>(&mut reader)?)
- } else if property == PriceFeedProperty::FundingTimestamp as u8 {
- PayloadPropertyValue::FundingTimestamp(read_option_timestamp::<BO>(
- &mut reader,
- )?)
- } else {
- bail!("unknown property");
- };
- feed.properties.push(value);
- }
- feeds.push(feed);
- }
- Ok(Self {
- timestamp_us,
- channel_id,
- feeds,
- })
- }
- }
- fn write_option_price<BO: ByteOrder>(
- mut writer: impl Write,
- value: Option<Price>,
- ) -> std::io::Result<()> {
- writer.write_i64::<BO>(value.map_or(0, |v| v.0.get()))
- }
- fn read_option_price<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<Price>> {
- let value = NonZeroI64::new(reader.read_i64::<BO>()?);
- Ok(value.map(Price))
- }
- fn write_option_rate<BO: ByteOrder>(
- mut writer: impl Write,
- value: Option<Rate>,
- ) -> std::io::Result<()> {
- match value {
- Some(value) => {
- writer.write_u8(1)?;
- writer.write_i64::<BO>(value.0)
- }
- None => {
- writer.write_u8(0)?;
- Ok(())
- }
- }
- }
- fn read_option_rate<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<Rate>> {
- let present = reader.read_u8()? != 0;
- if present {
- Ok(Some(Rate(reader.read_i64::<BO>()?)))
- } else {
- Ok(None)
- }
- }
- fn write_option_timestamp<BO: ByteOrder>(
- mut writer: impl Write,
- value: Option<TimestampUs>,
- ) -> std::io::Result<()> {
- match value {
- Some(value) => {
- writer.write_u8(1)?;
- writer.write_u64::<BO>(value.as_micros())
- }
- None => {
- writer.write_u8(0)?;
- Ok(())
- }
- }
- }
- fn read_option_timestamp<BO: ByteOrder>(
- mut reader: impl Read,
- ) -> std::io::Result<Option<TimestampUs>> {
- let present = reader.read_u8()? != 0;
- if present {
- Ok(Some(TimestampUs::from_micros(reader.read_u64::<BO>()?)))
- } else {
- Ok(None)
- }
- }
|