[hermes] add get price feed ids + refactor (#747)
* [Hermes] Add get price feed ids + refactor * Address feedbacks
This commit is contained in:
parent
32596d5d4e
commit
3ad3a46b1d
|
@ -43,6 +43,7 @@ pub async fn spawn(rpc_addr: String, store: Store) -> Result<()> {
|
|||
.route("/api/latest_vaas", get(rest::latest_vaas))
|
||||
.route("/api/get_vaa", get(rest::get_vaa))
|
||||
.route("/api/get_vaa_ccip", get(rest::get_vaa_ccip))
|
||||
.route("/api/price_feed_ids", get(rest::price_feed_ids))
|
||||
.with_state(state.clone());
|
||||
|
||||
// Listen in the background for new VAA's from the Wormhole RPC.
|
||||
|
|
|
@ -29,6 +29,10 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
/// PriceIdInput is a wrapper around a 32-byte hex string.
|
||||
/// that supports a flexible deserialization from a hex string.
|
||||
/// It supports both 0x-prefixed and non-prefixed hex strings,
|
||||
/// and also supports both lower and upper case characters.
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
pub struct PriceIdInput([u8; 32]);
|
||||
// TODO: Use const generics instead of macro.
|
||||
|
@ -42,6 +46,7 @@ impl From<PriceIdInput> for PriceIdentifier {
|
|||
|
||||
pub enum RestError {
|
||||
UpdateDataNotFound,
|
||||
CcipUpdateDataNotFound,
|
||||
}
|
||||
|
||||
impl IntoResponse for RestError {
|
||||
|
@ -50,10 +55,26 @@ impl IntoResponse for RestError {
|
|||
RestError::UpdateDataNotFound => {
|
||||
(StatusCode::NOT_FOUND, "Update data not found").into_response()
|
||||
}
|
||||
RestError::CcipUpdateDataNotFound => {
|
||||
// Returning Bad Gateway error because CCIP expects a 5xx error if it needs to
|
||||
// retry or try other endpoints. Bad Gateway seems the best choice here as this
|
||||
// is not an internal error and could happen on two scenarios:
|
||||
// 1. DB Api is not responding well (Bad Gateway is appropriate here)
|
||||
// 2. Publish time is a few seconds before current time and a VAA
|
||||
// Will be available in a few seconds. So we want the client to retry.
|
||||
|
||||
(StatusCode::BAD_GATEWAY, "CCIP update data not found").into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn price_feed_ids(
|
||||
State(state): State<super::State>,
|
||||
) -> Result<Json<Vec<PriceIdentifier>>, RestError> {
|
||||
let price_feeds = state.store.get_price_feed_ids();
|
||||
Ok(Json(price_feeds))
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct LatestVaasQueryParams {
|
||||
|
@ -173,7 +194,7 @@ pub async fn get_vaa_ccip(
|
|||
let price_feeds_with_update_data = state
|
||||
.store
|
||||
.get_price_feeds_with_update_data(vec![price_id], RequestTime::FirstAfter(publish_time))
|
||||
.map_err(|_| RestError::UpdateDataNotFound)?;
|
||||
.map_err(|_| RestError::CcipUpdateDataNotFound)?;
|
||||
|
||||
let vaa = price_feeds_with_update_data
|
||||
.update_data
|
||||
|
@ -199,6 +220,7 @@ pub async fn live() -> Result<impl IntoResponse, std::convert::Infallible> {
|
|||
pub async fn index() -> impl IntoResponse {
|
||||
Json([
|
||||
"/live",
|
||||
"/api/price_feed_ids",
|
||||
"/api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&..",
|
||||
"/api/latest_vaas?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&...",
|
||||
"/api/get_vaa?id=<price_feed_id>&publish_time=<publish_time_in_unix_timestamp>",
|
||||
|
|
|
@ -81,4 +81,8 @@ impl Store {
|
|||
request_time,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_price_feed_ids(&self) -> Vec<PriceIdentifier> {
|
||||
proof::batch_vaa::get_price_feed_ids(self.state.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ pub fn store_vaa_update(state: State, vaa_bytes: Vec<u8>) -> Result<()> {
|
|||
publish_time,
|
||||
};
|
||||
|
||||
let key = Key::new(price_feed.id.to_bytes().to_vec());
|
||||
let key = Key::BatchVaa(price_feed.id);
|
||||
state.insert(key, publish_time, StorageData::BatchVaa(price_info))?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -73,7 +73,7 @@ pub fn get_price_feeds_with_update_data(
|
|||
let mut price_feeds = HashMap::new();
|
||||
let mut vaas: HashSet<Vec<u8>> = HashSet::new();
|
||||
for price_id in price_ids {
|
||||
let key = Key::new(price_id.to_bytes().to_vec());
|
||||
let key = Key::BatchVaa(price_id);
|
||||
let maybe_data = state.get(key, request_time.clone())?;
|
||||
|
||||
match maybe_data {
|
||||
|
@ -82,7 +82,6 @@ pub fn get_price_feeds_with_update_data(
|
|||
vaas.insert(price_info.vaa_bytes);
|
||||
}
|
||||
None => {
|
||||
log::info!("No price feed found for price id: {:?}", price_id);
|
||||
return Err(anyhow!("No price feed found for price id: {:?}", price_id));
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +96,19 @@ pub fn get_price_feeds_with_update_data(
|
|||
}
|
||||
|
||||
|
||||
pub fn get_price_feed_ids(state: State) -> Vec<PriceIdentifier> {
|
||||
// Currently we have only one type and filter map is not necessary.
|
||||
// But we might have more types in the future.
|
||||
#[allow(clippy::unnecessary_filter_map)]
|
||||
state
|
||||
.keys()
|
||||
.into_iter()
|
||||
.filter_map(|key| match key {
|
||||
Key::BatchVaa(price_id) => Some(price_id),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Convert a PriceAttestation to a PriceFeed.
|
||||
///
|
||||
/// We cannot implmenet this function as From/Into trait because none of these types are defined in this crate.
|
||||
|
|
|
@ -9,6 +9,7 @@ use {
|
|||
Deref,
|
||||
DerefMut,
|
||||
},
|
||||
pyth_sdk::PriceIdentifier,
|
||||
};
|
||||
|
||||
pub mod local_cache;
|
||||
|
@ -18,13 +19,9 @@ pub enum StorageData {
|
|||
BatchVaa(PriceInfo),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash, Deref, DerefMut)]
|
||||
pub struct Key(Vec<u8>);
|
||||
|
||||
impl Key {
|
||||
pub fn new(key: Vec<u8>) -> Self {
|
||||
Self(key)
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Key {
|
||||
BatchVaa(PriceIdentifier),
|
||||
}
|
||||
|
||||
/// This trait defines the interface for update data storage
|
||||
|
@ -37,4 +34,5 @@ impl Key {
|
|||
pub trait Storage: Sync + Send {
|
||||
fn insert(&self, key: Key, time: UnixTimestamp, value: StorageData) -> Result<()>;
|
||||
fn get(&self, key: Key, request_time: RequestTime) -> Result<Option<StorageData>>;
|
||||
fn keys(&self) -> Vec<Key>;
|
||||
}
|
||||
|
|
|
@ -99,4 +99,8 @@ impl Storage for LocalCache {
|
|||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn keys(&self) -> Vec<Key> {
|
||||
self.cache.iter().map(|entry| entry.key().clone()).collect()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue