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: add publisher stake caps to validator #1778

Merged
merged 16 commits into from
Jul 31, 2024
2 changes: 1 addition & 1 deletion apps/hermes/server/src/state/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion pythnet/pythnet_sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
65 changes: 58 additions & 7 deletions pythnet/pythnet_sdk/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use borsh::{
#[cfg(feature = "quickcheck")]
use quickcheck::Arbitrary;
use {
crate::wire::PrefixedVec,
borsh::BorshSchema,
serde::{
Deserialize,
Expand All @@ -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)]
guibescos marked this conversation as resolved.
Show resolved Hide resolved
#[cfg_attr(
feature = "strum",
derive(strum::EnumDiscriminants),
Expand All @@ -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,
}
}
}
Expand All @@ -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)]
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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<u16, PublisherStakeCap>, // 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 {

Expand Down
4 changes: 2 additions & 2 deletions pythnet/pythnet_sdk/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
Expand Down
43 changes: 23 additions & 20 deletions target_chains/cosmwasm/contracts/pyth/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()])
}

Expand Down Expand Up @@ -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,
);
Expand All @@ -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,
);
Expand All @@ -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());
Expand All @@ -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]);
}
}

Expand All @@ -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,
);
Expand All @@ -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,
);
Expand All @@ -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,
);
Expand All @@ -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()]);

Expand All @@ -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()]);
Expand All @@ -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());
Expand Down Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion target_chains/near/receiver/tests/workspaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion target_chains/solana/programs/pyth-push-oracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();


Expand Down Expand Up @@ -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 {
Expand Down
Loading
Loading