Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update hermes to support publisher stake caps #1866

Merged
merged 13 commits into from
Sep 4, 2024
Merged
6 changes: 5 additions & 1 deletion apps/hermes/server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ where
types::RpcPriceIdentifier,
types::EncodingType,
types::PriceUpdate,
types::BinaryPriceUpdate,
types::BinaryUpdate,
types::ParsedPriceUpdate,
types::RpcPriceFeedMetadataV2,
types::PriceFeedMetadata,
Expand All @@ -150,6 +150,10 @@ where
.route("/api/get_price_feed", get(rest::get_price_feed))
.route("/api/get_vaa", get(rest::get_vaa))
.route("/api/get_vaa_ccip", get(rest::get_vaa_ccip))
.route(
guibescos marked this conversation as resolved.
Show resolved Hide resolved
"/api/get_publisher_stake_caps_update_data",
get(rest::get_publisher_stake_caps_update_data),
)
.route("/api/latest_price_feeds", get(rest::latest_price_feeds))
.route("/api/latest_vaas", get(rest::latest_vaas))
.route("/api/price_feed_ids", get(rest::price_feed_ids))
Expand Down
2 changes: 2 additions & 0 deletions apps/hermes/server/src/api/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use {
};

mod get_price_feed;
mod get_publisher_stake_caps_update_data;
mod get_vaa;
mod get_vaa_ccip;
mod index;
Expand All @@ -25,6 +26,7 @@ mod v2;

pub use {
get_price_feed::*,
get_publisher_stake_caps_update_data::*,
get_vaa::*,
get_vaa_ccip::*,
index::*,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use {
crate::{
api::{
rest::RestError,
types::{
BinaryUpdate,
EncodingType,
GetPublisherStakeCapsUpdateDataResponse,
ParsedPublisherStakeCapsUpdate,
},
ApiState,
},
state::Aggregates,
},
anyhow::Result,
axum::{
extract::State,
Json,
},
base64::{
engine::general_purpose::STANDARD as base64_standard_engine,
Engine as _,
},
serde::Deserialize,
serde_qs::axum::QsQuery,
utoipa::IntoParams,
};


#[derive(Debug, Deserialize, IntoParams)]
#[into_params(parameter_in=Query)]
pub struct GetPublisherStakeCapsUpdateData {
/// Optional encoding type. If true, return the message in the encoding specified by the encoding parameter. Default is `hex`.
#[serde(default)]
encoding: EncodingType,

/// If true, include the parsed update in the `parsed` field of each returned feed. Default is `true`.
#[serde(default = "default_true")]
parsed: bool,
}

fn default_true() -> bool {
true
}


/// Get the publisher stake caps update data
#[utoipa::path(
get,
path = "/api/get_publisher_stake_caps_update_data",
responses(
(status = 200, description = "Publisher stake caps update data retrieved succesfully", body = Vec<PriceFeedMetadata>)
),
params(
GetPublisherStakeCapsUpdateData
)
)]
pub async fn get_publisher_stake_caps_update_data<S>(
State(state): State<ApiState<S>>,
QsQuery(params): QsQuery<GetPublisherStakeCapsUpdateData>,
) -> Result<Json<GetPublisherStakeCapsUpdateDataResponse>, RestError>
where
S: Aggregates,
{
let state = &*state.state;
let publisher_stake_caps_with_update_data =
Aggregates::get_publisher_stake_caps_with_update_data(state)
.await
.map_err(|e| {
tracing::warn!(
"Error getting publisher stake caps with update data: {:?}",
e
);
RestError::UpdateDataNotFound
})?;

let encoded_data: Vec<String> = publisher_stake_caps_with_update_data
.update_data
.into_iter()
.map(|data| match params.encoding {
EncodingType::Base64 => base64_standard_engine.encode(data),
EncodingType::Hex => hex::encode(data),
})
.collect();

let binary = BinaryUpdate {
encoding: params.encoding,
data: encoded_data,
};

let parsed: Option<Vec<ParsedPublisherStakeCapsUpdate>> = if params.parsed {
Some(publisher_stake_caps_with_update_data.publisher_stake_caps)
} else {
None
};

Ok(Json(GetPublisherStakeCapsUpdateDataResponse {
binary,
parsed,
}))
}
4 changes: 2 additions & 2 deletions apps/hermes/server/src/api/rest/v2/latest_price_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use {
RestError,
},
types::{
BinaryPriceUpdate,
BinaryUpdate,
EncodingType,
ParsedPriceUpdate,
PriceIdInput,
Expand Down Expand Up @@ -108,7 +108,7 @@ where
EncodingType::Hex => hex::encode(data),
})
.collect();
let binary_price_update = BinaryPriceUpdate {
let binary_price_update = BinaryUpdate {
encoding: params.encoding,
data: encoded_data,
};
Expand Down
4 changes: 2 additions & 2 deletions apps/hermes/server/src/api/rest/v2/sse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use {
RestError,
},
types::{
BinaryPriceUpdate,
BinaryUpdate,
EncodingType,
ParsedPriceUpdate,
PriceIdInput,
Expand Down Expand Up @@ -211,7 +211,7 @@ where
.into_iter()
.map(|data| encoding.encode_str(&data))
.collect();
let binary_price_update = BinaryPriceUpdate {
let binary_price_update = BinaryUpdate {
encoding,
data: encoded_data,
};
Expand Down
4 changes: 2 additions & 2 deletions apps/hermes/server/src/api/rest/v2/timestamp_price_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {
RestError,
},
types::{
BinaryPriceUpdate,
BinaryUpdate,
EncodingType,
ParsedPriceUpdate,
PriceIdInput,
Expand Down Expand Up @@ -123,7 +123,7 @@ where
.into_iter()
.map(|data| query_params.encoding.encode_str(&data))
.collect();
let binary_price_update = BinaryPriceUpdate {
let binary_price_update = BinaryUpdate {
encoding: query_params.encoding,
data: encoded_data,
};
Expand Down
25 changes: 23 additions & 2 deletions apps/hermes/server/src/api/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use {
Deserialize,
Serialize,
},
solana_sdk::pubkey::Pubkey,
std::{
collections::BTreeMap,
fmt::{
Expand All @@ -40,6 +41,7 @@ use {
wormhole_sdk::Chain,
};


/// A price id is a 32-byte hex string, optionally prefixed with "0x".
/// Price ids are case insensitive.
///
Expand Down Expand Up @@ -231,7 +233,7 @@ impl EncodingType {
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct BinaryPriceUpdate {
pub struct BinaryUpdate {
pub encoding: EncodingType,
pub data: Vec<String>,
}
Expand Down Expand Up @@ -271,9 +273,28 @@ impl From<PriceFeedUpdate> for ParsedPriceUpdate {
}
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone)]
pub struct ParsedPublisherStakeCapsUpdate {
pub publisher_stake_caps: Vec<ParsedPublisherStakeCap>,
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone)]
pub struct ParsedPublisherStakeCap {
#[serde(with = "pyth_sdk::utils::as_string")]
pub publisher: Pubkey,
pub cap: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct GetPublisherStakeCapsUpdateDataResponse {
pub binary: BinaryUpdate,
#[serde(skip_serializing_if = "Option::is_none")]
pub parsed: Option<Vec<ParsedPublisherStakeCapsUpdate>>,
}
guibescos marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PriceUpdate {
pub binary: BinaryPriceUpdate,
pub binary: BinaryUpdate,
#[serde(skip_serializing_if = "Option::is_none")]
pub parsed: Option<Vec<ParsedPriceUpdate>>,
}
Expand Down
51 changes: 50 additions & 1 deletion apps/hermes/server/src/state/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ use {
WormholeMerkleState,
},
crate::{
api::types::{
ParsedPublisherStakeCap,
ParsedPublisherStakeCapsUpdate,
},
network::wormhole::VaaBytes,
state::{
benchmarks::Benchmarks,
Expand Down Expand Up @@ -45,6 +49,7 @@ use {
messages::{
Message,
MessageType,
PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID,
},
wire::{
from_slice,
Expand All @@ -55,6 +60,7 @@ use {
},
},
serde::Serialize,
solana_sdk::pubkey::Pubkey,
std::{
collections::HashSet,
time::Duration,
Expand All @@ -68,7 +74,6 @@ use {
},
wormhole_sdk::Vaa,
};

pub mod metrics;
pub mod wormhole_merkle;

Expand Down Expand Up @@ -215,6 +220,12 @@ pub struct PriceFeedsWithUpdateData {
pub update_data: Vec<Vec<u8>>,
}

#[derive(Debug, PartialEq)]
pub struct PublisherStakeCapsWithUpdateData {
pub publisher_stake_caps: Vec<ParsedPublisherStakeCapsUpdate>,
pub update_data: Vec<Vec<u8>>,
}

#[derive(Debug, Serialize)]
pub struct ReadinessMetadata {
pub has_completed_recently: bool,
Expand Down Expand Up @@ -242,6 +253,9 @@ where
price_ids: &[PriceIdentifier],
request_time: RequestTime,
) -> Result<PriceFeedsWithUpdateData>;
async fn get_publisher_stake_caps_with_update_data(
&self,
) -> Result<PublisherStakeCapsWithUpdateData>;
}

/// Allow downcasting State into CacheState for functions that depend on the `Cache` service.
Expand Down Expand Up @@ -403,6 +417,41 @@ where
}
}

async fn get_publisher_stake_caps_with_update_data(
&self,
) -> Result<PublisherStakeCapsWithUpdateData> {
let messages = self
.fetch_message_states(
vec![PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID],
RequestTime::Latest,
MessageStateFilter::Only(MessageType::PublisherStakeCapsMessage),
)
.await?;

let publisher_stake_caps = messages
.iter()
.map(|message_state| match message_state.message.clone() {
Message::PublisherStakeCapsMessage(message) => Ok(ParsedPublisherStakeCapsUpdate {
publisher_stake_caps: message
.caps
.iter()
.map(|cap| ParsedPublisherStakeCap {
publisher: Pubkey::from(cap.publisher),
cap: cap.cap,
})
.collect(),
}),
_ => Err(anyhow!("Invalid message state type")),
})
.collect::<Result<Vec<_>>>()?;

let update_data = construct_update_data(messages.into_iter().map(|m| m.into()).collect())?;
Ok(PublisherStakeCapsWithUpdateData {
publisher_stake_caps,
update_data,
})
}

async fn get_price_feed_ids(&self) -> HashSet<PriceIdentifier> {
guibescos marked this conversation as resolved.
Show resolved Hide resolved
Cache::message_state_keys(self)
.await
Expand Down
Loading