diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index de1d2d53a5..cd7ec93a49 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -69,7 +69,7 @@ impl MessageState { pub fn key(&self) -> MessageStateKey { MessageStateKey { feed_id: self.message.feed_id(), - type_: self.message.into(), + type_: self.message.clone().into(), } } diff --git a/pythnet/pythnet_sdk/Cargo.toml b/pythnet/pythnet_sdk/Cargo.toml index 99cb254675..e509744d1a 100644 --- a/pythnet/pythnet_sdk/Cargo.toml +++ b/pythnet/pythnet_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pythnet-sdk" -version = "2.1.0" +version = "2.2.0" description = "Pyth Runtime for Solana" authors = ["Pyth Data Association"] repository = "https://github.com/pyth-network/pythnet" diff --git a/pythnet/pythnet_sdk/src/messages.rs b/pythnet/pythnet_sdk/src/messages.rs index 7ab5b4bb0f..4444bb2717 100644 --- a/pythnet/pythnet_sdk/src/messages.rs +++ b/pythnet/pythnet_sdk/src/messages.rs @@ -11,6 +11,7 @@ use borsh::{ #[cfg(feature = "quickcheck")] use quickcheck::Arbitrary; use { + crate::wire::PrefixedVec, borsh::BorshSchema, serde::{ Deserialize, @@ -30,7 +31,7 @@ use { /// some of the methods for PriceFeedMessage and TwapMessage are not used by the oracle /// for the same reason. Rust compiler doesn't include the unused methods in the contract. /// Once we start using the unused structs and methods, the contract size will increase. -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr( feature = "strum", derive(strum::EnumDiscriminants), @@ -50,20 +51,28 @@ use { pub enum Message { PriceFeedMessage(PriceFeedMessage), TwapMessage(TwapMessage), + PublisherStakeCapsMessage(PublisherStakeCapsMessage), } +/// PublisherStakeCapsMessage is a global message that aggregates data from all price feeds +/// we can't associate it with a specific feed, so we use a feed id that is not used by any price feed +pub const PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID: FeedId = [1u8; 32]; + impl Message { pub fn publish_time(&self) -> i64 { match self { Self::PriceFeedMessage(msg) => msg.publish_time, Self::TwapMessage(msg) => msg.publish_time, + Self::PublisherStakeCapsMessage(msg) => msg.publish_time, } } + /// TO DO : This API doesn't work with PublisherStakeCapsMessage since it doesn't have a feed_id, consider refactoring pub fn feed_id(&self) -> FeedId { match self { Self::PriceFeedMessage(msg) => msg.feed_id, Self::TwapMessage(msg) => msg.feed_id, + Self::PublisherStakeCapsMessage(_) => PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID, } } } @@ -80,6 +89,7 @@ impl Arbitrary for Message { /// Id of a feed producing the message. One feed produces one or more messages. pub type FeedId = [u8; 32]; +pub type Pubkey = [u8; 32]; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, BorshSchema)] @@ -116,15 +126,15 @@ pub struct PriceFeedMessage { #[cfg(feature = "quickcheck")] impl Arbitrary for PriceFeedMessage { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut id = [0u8; 32]; - for item in &mut id { + let mut feed_id = [0u8; 32]; + for item in &mut feed_id { *item = u8::arbitrary(g); } let publish_time = i64::arbitrary(g); PriceFeedMessage { - id, + feed_id, price: i64::arbitrary(g), conf: u64::arbitrary(g), exponent: i32::arbitrary(g), @@ -153,15 +163,15 @@ pub struct TwapMessage { #[cfg(feature = "quickcheck")] impl Arbitrary for TwapMessage { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut id = [0u8; 32]; - for item in &mut id { + let mut feed_id = [0u8; 32]; + for item in &mut feed_id { *item = u8::arbitrary(g); } let publish_time = i64::arbitrary(g); TwapMessage { - id, + feed_id, cumulative_price: i128::arbitrary(g), cumulative_conf: u128::arbitrary(g), num_down_slots: u64::arbitrary(g), @@ -173,6 +183,47 @@ impl Arbitrary for TwapMessage { } } +#[repr(C)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PublisherStakeCapsMessage { + pub publish_time: i64, + pub caps: PrefixedVec, // PrefixedVec because we might have more than 256 publishers +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct PublisherStakeCap { + pub publisher: Pubkey, + pub cap: u64, +} + +#[cfg(feature = "quickcheck")] +impl Arbitrary for PublisherStakeCapsMessage { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let caps = Vec::arbitrary(g); + PublisherStakeCapsMessage { + publish_time: i64::arbitrary(g), + caps: caps.into(), + } + } +} + +#[cfg(feature = "quickcheck")] +impl Arbitrary for PublisherStakeCap { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + PublisherStakeCap { + publisher: { + let mut publisher = [0u8; 32]; + for item in &mut publisher { + *item = u8::arbitrary(g); + } + publisher + }, + cap: u64::arbitrary(g), + } + } +} + #[cfg(test)] mod tests { diff --git a/pythnet/pythnet_sdk/src/test_utils/mod.rs b/pythnet/pythnet_sdk/src/test_utils/mod.rs index a7f3fba938..2c9be05831 100644 --- a/pythnet/pythnet_sdk/src/test_utils/mod.rs +++ b/pythnet/pythnet_sdk/src/test_utils/mod.rs @@ -153,8 +153,8 @@ pub fn create_dummy_twap_message() -> Message { } pub fn create_accumulator_message( - all_feeds: &[Message], - updates: &[Message], + all_feeds: &[&Message], + updates: &[&Message], corrupt_wormhole_message: bool, corrupt_messages: bool, ) -> Vec { diff --git a/target_chains/cosmwasm/contracts/pyth/src/contract.rs b/target_chains/cosmwasm/contracts/pyth/src/contract.rs index 42a48a5a2a..f441e2a9e4 100644 --- a/target_chains/cosmwasm/contracts/pyth/src/contract.rs +++ b/target_chains/cosmwasm/contracts/pyth/src/contract.rs @@ -1155,7 +1155,7 @@ mod test { let feed1 = create_dummy_price_feed_message(100); let feed2 = create_dummy_price_feed_message(200); let feed3 = create_dummy_price_feed_message(300); - let data = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false, false); + let data = create_accumulator_message(&[&feed1, &feed2, &feed3], &[&feed1], false, false); check_sufficient_fee(&deps.as_ref(), &[data.into()]) } @@ -1246,21 +1246,22 @@ mod test { let feed2 = create_dummy_price_feed_message(200); let feed3 = create_dummy_price_feed_message(300); - let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false, false); + let msg = + create_accumulator_message(&[&feed1, &feed2, &feed3], &[&feed1, &feed3], false, false); assert_eq!( get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(), 200 ); - let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false, false); + let msg = create_accumulator_message(&[&feed1, &feed2, &feed3], &[&feed1], false, false); assert_eq!( get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(), 100 ); let msg = create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1, feed2, feed3, feed1, feed3], + &[&feed1, &feed2, &feed3], + &[&feed1, &feed2, &feed3, &feed1, &feed3], false, false, ); @@ -1272,8 +1273,8 @@ mod test { let batch_msg = create_batch_price_update_msg_from_attestations(vec![PriceAttestation::default()]); let msg = create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1, feed2, feed3], + &[&feed1, &feed2, &feed3], + &[&feed1, &feed2, &feed3], false, false, ); @@ -1293,7 +1294,7 @@ mod test { let feed1 = create_dummy_price_feed_message(100); let feed2 = create_dummy_price_feed_message(200); - let msg = create_accumulator_message(&[feed1, feed2], &[feed1], false, false); + let msg = create_accumulator_message(&[&feed1, &feed2], &[&feed1], false, false); let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_ok()); @@ -1310,12 +1311,13 @@ mod test { for i in 0..10000 { all_feeds.push(create_dummy_price_feed_message(i)); } + let all_feeds: Vec<&Message> = all_feeds.iter().collect(); let msg = create_accumulator_message(&all_feeds, &all_feeds[100..110], false, false); let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_ok()); for i in 100..110 { - check_price_match(&deps, &all_feeds[i]); + check_price_match(&deps, all_feeds[i]); } } @@ -1338,8 +1340,8 @@ mod test { let mut feed2 = create_dummy_price_feed_message(200); let mut feed3 = create_dummy_price_feed_message(300); let msg = create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1, feed2, feed3], + &[&feed1, &feed2, &feed3], + &[&feed1, &feed2, &feed3], false, false, ); @@ -1350,8 +1352,8 @@ mod test { as_mut_price_feed(&mut feed2).price *= 2; as_mut_price_feed(&mut feed3).price *= 2; let msg2 = create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1, feed2, feed3], + &[&feed1, &feed2, &feed3], + &[&feed1, &feed2, &feed3], false, false, ); @@ -1376,8 +1378,8 @@ mod test { as_mut_price_feed(&mut feed2).publish_time -= 1; as_mut_price_feed(&mut feed2).price *= 2; let msg = create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1, feed2, feed3], + &[&feed1, &feed2, &feed3], + &[&feed1, &feed2, &feed3], false, false, ); @@ -1400,10 +1402,11 @@ mod test { let feed3 = create_dummy_price_feed_message(300); as_mut_price_feed(&mut feed2).publish_time -= 1; as_mut_price_feed(&mut feed2).price *= 2; - let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false, false); + let msg = + create_accumulator_message(&[&feed1, &feed2, &feed3], &[&feed1, &feed3], false, false); let msg2 = - create_accumulator_message(&[feed1, feed2, feed3], &[feed2, feed3], false, false); + create_accumulator_message(&[&feed1, &feed2, &feed3], &[&feed2, &feed3], false, false); let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into(), msg2.into()]); @@ -1420,7 +1423,7 @@ mod test { .unwrap(); let feed1 = create_dummy_price_feed_message(100); - let mut msg = create_accumulator_message(&[feed1], &[feed1], false, false); + let mut msg = create_accumulator_message(&[&feed1], &[&feed1], false, false); msg[4] = 3; // major version let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); @@ -1439,7 +1442,7 @@ mod test { .unwrap(); let feed1 = create_dummy_price_feed_message(100); - let msg = create_accumulator_message(&[feed1], &[feed1], true, false); + let msg = create_accumulator_message(&[&feed1], &[&feed1], true, false); let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); @@ -1467,7 +1470,7 @@ mod test { prev_publish_time: 0, publish_slot: 0, }); - let msg = create_accumulator_message(&[feed1], &[feed1], false, false); + let msg = create_accumulator_message(&[&feed1], &[&feed1], false, false); let info = mock_info("123", &[]); let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); diff --git a/target_chains/near/receiver/tests/workspaces.rs b/target_chains/near/receiver/tests/workspaces.rs index 4b34951375..0cdf03b63a 100644 --- a/target_chains/near/receiver/tests/workspaces.rs +++ b/target_chains/near/receiver/tests/workspaces.rs @@ -910,7 +910,7 @@ async fn test_accumulator_updates() { // Create a couple of test feeds. let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1], false, false); + let message = create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1], false, false); let message = hex::encode(message); // Call the usual UpdatePriceFeed function. diff --git a/target_chains/solana/programs/pyth-push-oracle/src/lib.rs b/target_chains/solana/programs/pyth-push-oracle/src/lib.rs index 0bdfbfe744..94d02696a9 100644 --- a/target_chains/solana/programs/pyth-push-oracle/src/lib.rs +++ b/target_chains/solana/programs/pyth-push-oracle/src/lib.rs @@ -83,7 +83,7 @@ pub mod pyth_push_oracle { .map_err(|_| PushOracleError::DeserializeMessageFailed)?; let next_timestamp = match message { Message::PriceFeedMessage(price_feed_message) => price_feed_message.publish_time, - Message::TwapMessage(_) => { + Message::TwapMessage(_) | Message::PublisherStakeCapsMessage(_) => { return err!(PushOracleError::UnsupportedMessageType); } }; diff --git a/target_chains/solana/programs/pyth-push-oracle/tests/test_update_price_feed.rs b/target_chains/solana/programs/pyth-push-oracle/tests/test_update_price_feed.rs index 9bc11f1737..5dc69312c4 100644 --- a/target_chains/solana/programs/pyth-push-oracle/tests/test_update_price_feed.rs +++ b/target_chains/solana/programs/pyth-push-oracle/tests/test_update_price_feed.rs @@ -47,8 +47,8 @@ async fn test_update_price_feed() { let feed_2 = create_dummy_price_feed_message_with_feed_id(300, feed_id_2); let message = create_accumulator_message( - &[feed_1_old, feed_1_recent, feed_2], - &[feed_1_old, feed_1_recent, feed_2], + &[&feed_1_old, &feed_1_recent, &feed_2], + &[&feed_1_old, &feed_1_recent, &feed_2], false, false, ); diff --git a/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs b/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs index 868844bedd..5c83458cb3 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs @@ -437,7 +437,7 @@ fn post_price_update_from_vaa<'info>( price_update_account.price_message = price_feed_message; price_update_account.posted_slot = Clock::get()?.slot; } - Message::TwapMessage(_) => { + Message::TwapMessage(_) | Message::PublisherStakeCapsMessage(_) => { return err!(ReceiverError::UnsupportedMessageType); } } diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_price_update_from_vaa.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_price_update_from_vaa.rs index 1e81d452c4..6deb1837d4 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_price_update_from_vaa.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_price_update_from_vaa.rs @@ -55,7 +55,7 @@ async fn test_invalid_wormhole_message() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], true, false); + let message = create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], true, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let ProgramTestFixtures { @@ -100,7 +100,7 @@ async fn test_invalid_update_message() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, true); + let message = create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, true); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); @@ -150,15 +150,15 @@ async fn test_post_price_update_from_vaa() { let twap_1 = create_dummy_twap_message(); let message = create_accumulator_message( - &[feed_1, feed_2, twap_1], - &[feed_1, feed_2, twap_1], + &[&feed_1, &feed_2, &twap_1], + &[&feed_1, &feed_2, &twap_1], false, false, ); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); - let message2 = create_accumulator_message(&[feed_2, feed_3], &[feed_3], false, false); + let message2 = create_accumulator_message(&[&feed_2, &feed_3], &[&feed_3], false, false); let (_, merkle_price_updates2) = deserialize_accumulator_update_data(message2).unwrap(); let ProgramTestFixtures { diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs index 0becd32553..1556cd31e0 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs @@ -42,7 +42,8 @@ use { async fn test_post_update() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); @@ -185,7 +186,8 @@ async fn test_post_update() { async fn test_post_update_wrong_encoded_vaa_owner() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let ProgramTestFixtures { @@ -226,7 +228,8 @@ async fn test_post_update_wrong_encoded_vaa_owner() { async fn test_post_update_wrong_setup() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let ProgramTestFixtures { diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs index e4c0d9d78c..6cd2cf02b7 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs @@ -44,7 +44,8 @@ use { async fn test_post_update_atomic() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let vaa = serde_wormhole::to_vec(&trim_vaa_signatures( serde_wormhole::from_slice(&vaa).unwrap(), @@ -209,7 +210,8 @@ async fn test_post_update_atomic() { async fn test_post_update_atomic_wrong_vaa() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let ProgramTestFixtures { @@ -458,7 +460,8 @@ async fn test_post_update_atomic_wrong_vaa() { async fn test_post_update_atomic_wrong_setup() { let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false); + let message = + create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1, &feed_2], false, false); let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); let price_update_keypair = Keypair::new();