1#[cfg(feature = "solana-program")]
2use anchor_lang::{AnchorDeserialize, AnchorSerialize};
3#[cfg(not(feature = "solana-program"))]
4use borsh::{BorshDeserialize, BorshSerialize};
5#[cfg(feature = "quickcheck")]
6use quickcheck::Arbitrary;
7use {
8 crate::wire::PrefixedVec,
9 borsh::BorshSchema,
10 serde::{Deserialize, Serialize},
11};
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26#[cfg_attr(
27 feature = "strum",
28 derive(strum::EnumDiscriminants),
29 strum_discriminants(name(MessageType)),
30 strum_discriminants(vis(pub)),
31 strum_discriminants(derive(
32 Hash,
33 strum::EnumIter,
34 strum::EnumString,
35 strum::IntoStaticStr,
36 strum::Display,
37 Serialize,
38 Deserialize
39 ))
40)]
41
42pub enum Message {
43 PriceFeedMessage(PriceFeedMessage),
44 TwapMessage(TwapMessage),
45 PublisherStakeCapsMessage(PublisherStakeCapsMessage),
46}
47
48pub const PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID: FeedId = [1u8; 32];
51
52impl Message {
53 pub fn publish_time(&self) -> i64 {
54 match self {
55 Self::PriceFeedMessage(msg) => msg.publish_time,
56 Self::TwapMessage(msg) => msg.publish_time,
57 Self::PublisherStakeCapsMessage(msg) => msg.publish_time,
58 }
59 }
60
61 pub fn feed_id(&self) -> FeedId {
63 match self {
64 Self::PriceFeedMessage(msg) => msg.feed_id,
65 Self::TwapMessage(msg) => msg.feed_id,
66 Self::PublisherStakeCapsMessage(_) => PUBLISHER_STAKE_CAPS_MESSAGE_FEED_ID,
67 }
68 }
69}
70
71#[cfg(feature = "quickcheck")]
72impl Arbitrary for Message {
73 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
74 match u8::arbitrary(g) % 2 {
75 0 => Message::PriceFeedMessage(Arbitrary::arbitrary(g)),
76 _ => Message::TwapMessage(Arbitrary::arbitrary(g)),
77 }
78 }
79}
80
81pub type FeedId = [u8; 32];
83pub type Pubkey = [u8; 32];
84
85#[repr(C)]
86#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, BorshSchema)]
87#[cfg_attr(feature = "solana-program", derive(AnchorSerialize, AnchorDeserialize))]
88#[cfg_attr(
89 not(feature = "solana-program"),
90 derive(BorshSerialize, BorshDeserialize)
91)]
92
93pub struct PriceFeedMessage {
94 pub feed_id: [u8; 32],
96 pub price: i64,
97 pub conf: u64,
98 pub exponent: i32,
99 pub publish_time: i64,
101 pub prev_publish_time: i64,
114 pub ema_price: i64,
115 pub ema_conf: u64,
116}
117
118#[cfg(feature = "quickcheck")]
119impl Arbitrary for PriceFeedMessage {
120 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
121 let mut feed_id = [0u8; 32];
122 for item in &mut feed_id {
123 *item = u8::arbitrary(g);
124 }
125
126 let publish_time = i64::arbitrary(g);
127
128 PriceFeedMessage {
129 feed_id,
130 price: i64::arbitrary(g),
131 conf: u64::arbitrary(g),
132 exponent: i32::arbitrary(g),
133 publish_time,
134 prev_publish_time: publish_time.saturating_sub(i64::arbitrary(g)),
135 ema_price: i64::arbitrary(g),
136 ema_conf: u64::arbitrary(g),
137 }
138 }
139}
140
141#[repr(C)]
143#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
144pub struct TwapMessage {
145 pub feed_id: FeedId,
146 pub cumulative_price: i128,
147 pub cumulative_conf: u128,
148 pub num_down_slots: u64,
149 pub exponent: i32,
150 pub publish_time: i64,
151 pub prev_publish_time: i64,
152 pub publish_slot: u64,
153}
154
155#[cfg(feature = "quickcheck")]
156impl Arbitrary for TwapMessage {
157 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
158 let mut feed_id = [0u8; 32];
159 for item in &mut feed_id {
160 *item = u8::arbitrary(g);
161 }
162
163 let publish_time = i64::arbitrary(g);
164
165 TwapMessage {
166 feed_id,
167 cumulative_price: i128::arbitrary(g),
168 cumulative_conf: u128::arbitrary(g),
169 num_down_slots: u64::arbitrary(g),
170 exponent: i32::arbitrary(g),
171 publish_time,
172 prev_publish_time: publish_time.saturating_sub(i64::arbitrary(g)),
173 publish_slot: u64::arbitrary(g),
174 }
175 }
176}
177
178#[repr(C)]
179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
180pub struct PublisherStakeCapsMessage {
181 pub publish_time: i64,
182 pub caps: PrefixedVec<u16, PublisherStakeCap>, }
184
185#[repr(C)]
186#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
187pub struct PublisherStakeCap {
188 pub publisher: Pubkey,
189 pub cap: u64,
190}
191
192#[cfg(feature = "quickcheck")]
193impl Arbitrary for PublisherStakeCapsMessage {
194 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
195 let caps = Vec::arbitrary(g);
196 PublisherStakeCapsMessage {
197 publish_time: i64::arbitrary(g),
198 caps: caps.into(),
199 }
200 }
201}
202
203#[cfg(feature = "quickcheck")]
204impl Arbitrary for PublisherStakeCap {
205 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
206 PublisherStakeCap {
207 publisher: {
208 let mut publisher = [0u8; 32];
209 for item in &mut publisher {
210 *item = u8::arbitrary(g);
211 }
212 publisher
213 },
214 cap: u64::arbitrary(g),
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221
222 use crate::{
223 messages::{Message, PriceFeedMessage},
224 wire::Serializer,
225 };
226
227 #[test]
229 fn test_forward_compatibility() {
230 use {serde::Serialize, std::iter};
231 let msg = Message::PriceFeedMessage(PriceFeedMessage {
232 feed_id: [1u8; 32],
233 price: 1,
234 conf: 1,
235 exponent: 1,
236 publish_time: 1,
237 prev_publish_time: 1,
238 ema_price: 1,
239 ema_conf: 1,
240 });
241 let mut buffer = Vec::new();
242 let mut cursor = std::io::Cursor::new(&mut buffer);
243 let mut serializer: Serializer<_, byteorder::LE> = Serializer::new(&mut cursor);
244 msg.serialize(&mut serializer).unwrap();
245 buffer.extend(iter::repeat(0).take(10));
246 let deserialized = crate::wire::from_slice::<byteorder::LE, Message>(&buffer).unwrap();
247 assert_eq!(deserialized, msg);
248 }
249}