Skip to content

Commit 3ad3a46

Browse files
authored
[hermes] add get price feed ids + refactor (#747)
* [Hermes] Add get price feed ids + refactor * Address feedbacks
1 parent 32596d5 commit 3ad3a46

File tree

6 files changed

+52
-11
lines changed

6 files changed

+52
-11
lines changed

hermes/src/network/rpc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub async fn spawn(rpc_addr: String, store: Store) -> Result<()> {
4343
.route("/api/latest_vaas", get(rest::latest_vaas))
4444
.route("/api/get_vaa", get(rest::get_vaa))
4545
.route("/api/get_vaa_ccip", get(rest::get_vaa_ccip))
46+
.route("/api/price_feed_ids", get(rest::price_feed_ids))
4647
.with_state(state.clone());
4748

4849
// Listen in the background for new VAA's from the Wormhole RPC.

hermes/src/network/rpc/rest.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ use {
2929
},
3030
};
3131

32+
/// PriceIdInput is a wrapper around a 32-byte hex string.
33+
/// that supports a flexible deserialization from a hex string.
34+
/// It supports both 0x-prefixed and non-prefixed hex strings,
35+
/// and also supports both lower and upper case characters.
3236
#[derive(Debug, Clone, Deref, DerefMut)]
3337
pub struct PriceIdInput([u8; 32]);
3438
// TODO: Use const generics instead of macro.
@@ -42,6 +46,7 @@ impl From<PriceIdInput> for PriceIdentifier {
4246

4347
pub enum RestError {
4448
UpdateDataNotFound,
49+
CcipUpdateDataNotFound,
4550
}
4651

4752
impl IntoResponse for RestError {
@@ -50,10 +55,26 @@ impl IntoResponse for RestError {
5055
RestError::UpdateDataNotFound => {
5156
(StatusCode::NOT_FOUND, "Update data not found").into_response()
5257
}
58+
RestError::CcipUpdateDataNotFound => {
59+
// Returning Bad Gateway error because CCIP expects a 5xx error if it needs to
60+
// retry or try other endpoints. Bad Gateway seems the best choice here as this
61+
// is not an internal error and could happen on two scenarios:
62+
// 1. DB Api is not responding well (Bad Gateway is appropriate here)
63+
// 2. Publish time is a few seconds before current time and a VAA
64+
// Will be available in a few seconds. So we want the client to retry.
65+
66+
(StatusCode::BAD_GATEWAY, "CCIP update data not found").into_response()
67+
}
5368
}
5469
}
5570
}
5671

72+
pub async fn price_feed_ids(
73+
State(state): State<super::State>,
74+
) -> Result<Json<Vec<PriceIdentifier>>, RestError> {
75+
let price_feeds = state.store.get_price_feed_ids();
76+
Ok(Json(price_feeds))
77+
}
5778

5879
#[derive(Debug, serde::Deserialize)]
5980
pub struct LatestVaasQueryParams {
@@ -173,7 +194,7 @@ pub async fn get_vaa_ccip(
173194
let price_feeds_with_update_data = state
174195
.store
175196
.get_price_feeds_with_update_data(vec![price_id], RequestTime::FirstAfter(publish_time))
176-
.map_err(|_| RestError::UpdateDataNotFound)?;
197+
.map_err(|_| RestError::CcipUpdateDataNotFound)?;
177198

178199
let vaa = price_feeds_with_update_data
179200
.update_data
@@ -199,6 +220,7 @@ pub async fn live() -> Result<impl IntoResponse, std::convert::Infallible> {
199220
pub async fn index() -> impl IntoResponse {
200221
Json([
201222
"/live",
223+
"/api/price_feed_ids",
202224
"/api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&..",
203225
"/api/latest_vaas?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&...",
204226
"/api/get_vaa?id=<price_feed_id>&publish_time=<publish_time_in_unix_timestamp>",

hermes/src/store.rs

+4
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,8 @@ impl Store {
8181
request_time,
8282
)
8383
}
84+
85+
pub fn get_price_feed_ids(&self) -> Vec<PriceIdentifier> {
86+
proof::batch_vaa::get_price_feed_ids(self.state.clone())
87+
}
8488
}

hermes/src/store/proof/batch_vaa.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub fn store_vaa_update(state: State, vaa_bytes: Vec<u8>) -> Result<()> {
5858
publish_time,
5959
};
6060

61-
let key = Key::new(price_feed.id.to_bytes().to_vec());
61+
let key = Key::BatchVaa(price_feed.id);
6262
state.insert(key, publish_time, StorageData::BatchVaa(price_info))?;
6363
}
6464
Ok(())
@@ -73,7 +73,7 @@ pub fn get_price_feeds_with_update_data(
7373
let mut price_feeds = HashMap::new();
7474
let mut vaas: HashSet<Vec<u8>> = HashSet::new();
7575
for price_id in price_ids {
76-
let key = Key::new(price_id.to_bytes().to_vec());
76+
let key = Key::BatchVaa(price_id);
7777
let maybe_data = state.get(key, request_time.clone())?;
7878

7979
match maybe_data {
@@ -82,7 +82,6 @@ pub fn get_price_feeds_with_update_data(
8282
vaas.insert(price_info.vaa_bytes);
8383
}
8484
None => {
85-
log::info!("No price feed found for price id: {:?}", price_id);
8685
return Err(anyhow!("No price feed found for price id: {:?}", price_id));
8786
}
8887
}
@@ -97,6 +96,19 @@ pub fn get_price_feeds_with_update_data(
9796
}
9897

9998

99+
pub fn get_price_feed_ids(state: State) -> Vec<PriceIdentifier> {
100+
// Currently we have only one type and filter map is not necessary.
101+
// But we might have more types in the future.
102+
#[allow(clippy::unnecessary_filter_map)]
103+
state
104+
.keys()
105+
.into_iter()
106+
.filter_map(|key| match key {
107+
Key::BatchVaa(price_id) => Some(price_id),
108+
})
109+
.collect()
110+
}
111+
100112
/// Convert a PriceAttestation to a PriceFeed.
101113
///
102114
/// We cannot implmenet this function as From/Into trait because none of these types are defined in this crate.

hermes/src/store/storage.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use {
99
Deref,
1010
DerefMut,
1111
},
12+
pyth_sdk::PriceIdentifier,
1213
};
1314

1415
pub mod local_cache;
@@ -18,13 +19,9 @@ pub enum StorageData {
1819
BatchVaa(PriceInfo),
1920
}
2021

21-
#[derive(Clone, PartialEq, Eq, Debug, Hash, Deref, DerefMut)]
22-
pub struct Key(Vec<u8>);
23-
24-
impl Key {
25-
pub fn new(key: Vec<u8>) -> Self {
26-
Self(key)
27-
}
22+
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
23+
pub enum Key {
24+
BatchVaa(PriceIdentifier),
2825
}
2926

3027
/// This trait defines the interface for update data storage
@@ -37,4 +34,5 @@ impl Key {
3734
pub trait Storage: Sync + Send {
3835
fn insert(&self, key: Key, time: UnixTimestamp, value: StorageData) -> Result<()>;
3936
fn get(&self, key: Key, request_time: RequestTime) -> Result<Option<StorageData>>;
37+
fn keys(&self) -> Vec<Key>;
4038
}

hermes/src/store/storage/local_cache.rs

+4
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,8 @@ impl Storage for LocalCache {
9999
None => Ok(None),
100100
}
101101
}
102+
103+
fn keys(&self) -> Vec<Key> {
104+
self.cache.iter().map(|entry| entry.key().clone()).collect()
105+
}
102106
}

0 commit comments

Comments
 (0)