Skip to content

Commit 7258ea4

Browse files
committed
update to sync with latest pyth lazer contract
1 parent 57ed54b commit 7258ea4

File tree

8 files changed

+5374
-764
lines changed

8 files changed

+5374
-764
lines changed

lazer/Cargo.lock

Lines changed: 5224 additions & 648 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lazer/contracts/solana/programs/pyth-lazer-solana-contract/src/lib.rs

Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,16 @@ mod signature;
22

33
use {
44
crate::signature::VerifiedMessage,
5-
anchor_lang::{
6-
prelude::*, solana_program::pubkey::PUBKEY_BYTES, system_program, Discriminator,
7-
},
8-
std::{io::Cursor, mem::size_of},
5+
anchor_lang::{prelude::*, solana_program::pubkey::PUBKEY_BYTES, system_program},
6+
solana_program::pubkey,
7+
std::mem::size_of,
98
};
109

1110
pub use {
1211
crate::signature::{ed25519_program_args, Ed25519SignatureOffsets},
1312
pyth_lazer_protocol as protocol,
1413
};
1514

16-
use solana_program::pubkey;
17-
1815
declare_id!("pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt");
1916

2017
pub const STORAGE_ID: Pubkey = pubkey!("3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL");
@@ -42,24 +39,6 @@ impl TrustedSignerInfo {
4239
const SERIALIZED_LEN: usize = PUBKEY_BYTES + size_of::<i64>();
4340
}
4441

45-
/// TODO: remove this legacy storage type
46-
#[derive(AnchorDeserialize)]
47-
pub struct StorageV010 {
48-
pub top_authority: Pubkey,
49-
pub num_trusted_signers: u8,
50-
pub trusted_signers: [TrustedSignerInfo; MAX_NUM_TRUSTED_SIGNERS],
51-
}
52-
53-
impl StorageV010 {
54-
pub const SERIALIZED_LEN: usize = PUBKEY_BYTES
55-
+ size_of::<u8>()
56-
+ TrustedSignerInfo::SERIALIZED_LEN * MAX_NUM_TRUSTED_SIGNERS;
57-
58-
pub fn initialized_trusted_signers(&self) -> &[TrustedSignerInfo] {
59-
&self.trusted_signers[0..usize::from(self.num_trusted_signers)]
60-
}
61-
}
62-
6342
#[account]
6443
pub struct Storage {
6544
pub top_authority: Pubkey,
@@ -100,40 +79,6 @@ pub mod pyth_lazer_solana_contract {
10079
Ok(())
10180
}
10281

103-
pub fn migrate_from_0_1_0(ctx: Context<MigrateFrom010>, treasury: Pubkey) -> Result<()> {
104-
let old_data = ctx.accounts.storage.data.borrow();
105-
if old_data[0..ANCHOR_DISCRIMINATOR_BYTES] != Storage::DISCRIMINATOR {
106-
return Err(ProgramError::InvalidAccountData.into());
107-
}
108-
let old_storage = StorageV010::deserialize(&mut &old_data[ANCHOR_DISCRIMINATOR_BYTES..])?;
109-
if old_storage.top_authority != ctx.accounts.top_authority.key() {
110-
return Err(ProgramError::MissingRequiredSignature.into());
111-
}
112-
drop(old_data);
113-
114-
let space = ANCHOR_DISCRIMINATOR_BYTES + Storage::SERIALIZED_LEN;
115-
ctx.accounts.storage.realloc(space, false)?;
116-
let min_lamports = Rent::get()?.minimum_balance(space);
117-
if ctx.accounts.storage.lamports() < min_lamports {
118-
return Err(ProgramError::AccountNotRentExempt.into());
119-
}
120-
121-
let mut new_storage = Storage {
122-
top_authority: old_storage.top_authority,
123-
treasury,
124-
single_update_fee_in_lamports: 1,
125-
num_trusted_signers: old_storage.num_trusted_signers,
126-
trusted_signers: Default::default(),
127-
_extra_space: [0; EXTRA_SPACE],
128-
};
129-
new_storage.trusted_signers[..old_storage.trusted_signers.len()]
130-
.copy_from_slice(&old_storage.trusted_signers);
131-
new_storage.try_serialize(&mut Cursor::new(
132-
&mut **ctx.accounts.storage.data.borrow_mut(),
133-
))?;
134-
Ok(())
135-
}
136-
13782
pub fn update(ctx: Context<Update>, trusted_signer: Pubkey, expires_at: i64) -> Result<()> {
13883
let num_trusted_signers: usize = ctx.accounts.storage.num_trusted_signers.into();
13984
if num_trusted_signers > ctx.accounts.storage.trusted_signers.len() {
@@ -196,7 +141,6 @@ pub mod pyth_lazer_solana_contract {
196141
message_data: Vec<u8>,
197142
ed25519_instruction_index: u16,
198143
signature_index: u8,
199-
message_offset: u16,
200144
) -> Result<VerifiedMessage> {
201145
system_program::transfer(
202146
CpiContext::new(
@@ -215,7 +159,6 @@ pub mod pyth_lazer_solana_contract {
215159
&message_data,
216160
ed25519_instruction_index,
217161
signature_index,
218-
message_offset,
219162
)
220163
.map_err(|err| {
221164
msg!("signature verification error: {:?}", err);
@@ -230,15 +173,13 @@ pub fn verify_message_direct<'a>(
230173
message_data: &'a [u8],
231174
ed25519_instruction_index: u16,
232175
signature_index: u8,
233-
message_offset: u16,
234176
) -> Result<VerifiedMessage> {
235177
signature::verify_message(
236178
pyth_storage_account,
237179
instruction_sysvar,
238180
&message_data,
239181
ed25519_instruction_index,
240182
signature_index,
241-
message_offset,
242183
)
243184
.map_err(|err| {
244185
msg!("signature verification error: {:?}", err);
@@ -261,19 +202,6 @@ pub struct Initialize<'info> {
261202
pub system_program: Program<'info, System>,
262203
}
263204

264-
#[derive(Accounts)]
265-
pub struct MigrateFrom010<'info> {
266-
pub top_authority: Signer<'info>,
267-
#[account(
268-
mut,
269-
seeds = [STORAGE_SEED],
270-
bump,
271-
)]
272-
/// CHECK: top_authority in storage must match top_authority account.
273-
pub storage: AccountInfo<'info>,
274-
pub system_program: Program<'info, System>,
275-
}
276-
277205
#[derive(Accounts)]
278206
pub struct Update<'info> {
279207
pub top_authority: Signer<'info>,

lazer/contracts/solana/programs/pyth-lazer-solana-contract/src/signature.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use {
22
crate::Storage,
33
anchor_lang::{
44
prelude::{borsh, AccountInfo, Clock, ProgramError, Pubkey, SolanaSysvar},
5-
solana_program::{ed25519_program, pubkey::PUBKEY_BYTES, sysvar},
5+
solana_program::{
6+
ed25519_program, program_memory::sol_memcmp, pubkey::PUBKEY_BYTES, sysvar,
7+
},
68
AnchorDeserialize, AnchorSerialize,
79
},
810
bytemuck::{cast_slice, checked::try_cast_slice, Pod, Zeroable},
@@ -127,6 +129,8 @@ pub enum SignatureVerificationError {
127129
InvalidStorageData,
128130
#[error("not a trusted signer")]
129131
NotTrustedSigner,
132+
#[error("invalid message data")]
133+
InvalidMessageData,
130134
}
131135

132136
impl From<SignatureVerificationError> for ProgramError {
@@ -148,6 +152,10 @@ impl From<SignatureVerificationError> for anchor_lang::error::Error {
148152
}
149153
}
150154

155+
fn slice_eq(a: &[u8], b: &[u8]) -> bool {
156+
a.len() == b.len() && sol_memcmp(a, b, a.len()) == 0
157+
}
158+
151159
/// Verifies a ed25519 signature on Solana by checking that the transaction contains
152160
/// a correct call to the built-in `ed25519_program`.
153161
///
@@ -163,7 +171,6 @@ pub fn verify_message(
163171
message_data: &[u8],
164172
ed25519_instruction_index: u16,
165173
signature_index: u8,
166-
message_offset: u16,
167174
) -> Result<VerifiedMessage, SignatureVerificationError> {
168175
const SOLANA_FORMAT_MAGIC_LE: u32 = 2182742457;
169176

@@ -175,25 +182,25 @@ pub fn verify_message(
175182
return Err(SignatureVerificationError::Ed25519InstructionMustPrecedeCurrentInstruction);
176183
}
177184

178-
let instruction = sysvar::instructions::load_instruction_at_checked(
185+
let ed25519_instruction = sysvar::instructions::load_instruction_at_checked(
179186
ed25519_instruction_index.into(),
180187
instructions_sysvar,
181188
)
182189
.map_err(SignatureVerificationError::LoadInstructionAtFailed)?;
183190

184-
if instruction.program_id != ed25519_program::ID {
191+
if ed25519_instruction.program_id != ed25519_program::ID {
185192
return Err(SignatureVerificationError::InvalidEd25519InstructionProgramId);
186193
}
187-
if instruction.data.len() < ED25519_PROGRAM_INPUT_HEADER_LEN {
194+
if ed25519_instruction.data.len() < ED25519_PROGRAM_INPUT_HEADER_LEN {
188195
return Err(SignatureVerificationError::InvalidEd25519InstructionDataLength);
189196
}
190197

191-
let num_signatures = instruction.data[0];
198+
let num_signatures = ed25519_instruction.data[0];
192199
if signature_index >= num_signatures {
193200
return Err(SignatureVerificationError::InvalidSignatureIndex);
194201
}
195202
let args: &[Ed25519SignatureOffsets] =
196-
try_cast_slice(&instruction.data[ED25519_PROGRAM_INPUT_HEADER_LEN..])
203+
try_cast_slice(&ed25519_instruction.data[ED25519_PROGRAM_INPUT_HEADER_LEN..])
197204
.map_err(|_| SignatureVerificationError::InvalidEd25519InstructionDataLength)?;
198205

199206
let args_len = args
@@ -205,19 +212,37 @@ pub fn verify_message(
205212
}
206213
let offsets = &args[usize::from(signature_index)];
207214

208-
let expected_signature_offset = message_offset
209-
.checked_add(MAGIC_LEN)
215+
let message_offset = offsets
216+
.signature_offset
217+
.checked_sub(MAGIC_LEN)
210218
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
211-
if offsets.signature_offset != expected_signature_offset {
212-
return Err(SignatureVerificationError::InvalidSignatureOffset);
219+
220+
let self_instruction = sysvar::instructions::load_instruction_at_checked(
221+
self_instruction_index.into(),
222+
instructions_sysvar,
223+
)
224+
.map_err(SignatureVerificationError::LoadInstructionAtFailed)?;
225+
226+
let message_end_offset = offsets
227+
.message_data_offset
228+
.checked_add(offsets.message_data_size)
229+
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
230+
let expected_message_data = self_instruction
231+
.data
232+
.get(usize::from(message_offset)..usize::from(message_end_offset))
233+
.ok_or(SignatureVerificationError::InvalidMessageOffset)?;
234+
235+
if !slice_eq(expected_message_data, message_data) {
236+
return Err(SignatureVerificationError::InvalidMessageData);
213237
}
214238

215239
let magic = LE::read_u32(&message_data[..MAGIC_LEN.into()]);
216240
if magic != SOLANA_FORMAT_MAGIC_LE {
217241
return Err(SignatureVerificationError::FormatMagicMismatch);
218242
}
219243

220-
let expected_public_key_offset = expected_signature_offset
244+
let expected_public_key_offset = offsets
245+
.signature_offset
221246
.checked_add(SIGNATURE_LEN)
222247
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
223248
if offsets.public_key_offset != expected_public_key_offset {

lazer/sdk/rust/protocol/src/api.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use crate::router::{
4+
Chain, Channel, JsonBinaryEncoding, JsonUpdate, PriceFeedId, PriceFeedProperty,
5+
};
6+
7+
#[derive(Debug, Clone, Serialize, Deserialize)]
8+
#[serde(rename_all = "camelCase")]
9+
pub struct LatestPriceRequest {
10+
pub price_feed_ids: Vec<PriceFeedId>,
11+
pub properties: Vec<PriceFeedProperty>,
12+
pub chains: Vec<Chain>,
13+
#[serde(default)]
14+
pub json_binary_encoding: JsonBinaryEncoding,
15+
/// If `true`, the stream update will contain a JSON object containing
16+
/// all data of the update.
17+
#[serde(default = "default_parsed")]
18+
pub parsed: bool,
19+
pub channel: Channel,
20+
}
21+
22+
#[derive(Debug, Clone, Serialize, Deserialize)]
23+
#[serde(rename_all = "camelCase")]
24+
pub struct ReducePriceRequest {
25+
pub payload: JsonUpdate,
26+
pub price_feed_ids: Vec<PriceFeedId>,
27+
}
28+
29+
pub type LatestPriceResponse = JsonUpdate;
30+
pub type ReducePriceResponse = JsonUpdate;
31+
32+
pub fn default_parsed() -> bool {
33+
true
34+
}

lazer/sdk/rust/protocol/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Protocol types.
22
3+
pub mod api;
34
pub mod message;
45
pub mod payload;
56
pub mod publisher;

lazer/sdk/rust/protocol/src/payload.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use {
55
crate::router::{ChannelId, Price},
66
anyhow::bail,
77
byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt, BE, LE},
8+
serde::{Deserialize, Serialize},
89
std::{
910
io::{Cursor, Read, Write},
1011
num::NonZeroI64,
@@ -32,13 +33,16 @@ pub enum PayloadPropertyValue {
3233
Price(Option<Price>),
3334
BestBidPrice(Option<Price>),
3435
BestAskPrice(Option<Price>),
36+
PublisherCount(Option<u16>),
37+
Exponent(i16),
3538
}
3639

37-
#[derive(Debug, Clone, Default)]
40+
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3841
pub struct AggregatedPriceFeedData {
3942
pub price: Option<Price>,
4043
pub best_bid_price: Option<Price>,
4144
pub best_ask_price: Option<Price>,
45+
pub publisher_count: Option<u16>,
4246
}
4347

4448
pub const PAYLOAD_FORMAT_MAGIC: u32 = 2479346549;
@@ -47,15 +51,15 @@ impl PayloadData {
4751
pub fn new(
4852
timestamp_us: TimestampUs,
4953
channel_id: ChannelId,
50-
feeds: &[(PriceFeedId, AggregatedPriceFeedData)],
54+
feeds: &[(PriceFeedId, i16, AggregatedPriceFeedData)],
5155
requested_properties: &[PriceFeedProperty],
5256
) -> Self {
5357
Self {
5458
timestamp_us,
5559
channel_id,
5660
feeds: feeds
5761
.iter()
58-
.map(|(feed_id, feed)| PayloadFeedData {
62+
.map(|(feed_id, exponent, feed)| PayloadFeedData {
5963
feed_id: *feed_id,
6064
properties: requested_properties
6165
.iter()
@@ -67,6 +71,12 @@ impl PayloadData {
6771
PriceFeedProperty::BestAskPrice => {
6872
PayloadPropertyValue::BestAskPrice(feed.best_ask_price)
6973
}
74+
PriceFeedProperty::PublisherCount => {
75+
PayloadPropertyValue::PublisherCount(feed.publisher_count)
76+
}
77+
PriceFeedProperty::Exponent => {
78+
PayloadPropertyValue::Exponent(*exponent)
79+
}
7080
})
7181
.collect(),
7282
})
@@ -96,6 +106,14 @@ impl PayloadData {
96106
writer.write_u8(PriceFeedProperty::BestAskPrice as u8)?;
97107
write_option_price::<BO>(&mut writer, *price)?;
98108
}
109+
PayloadPropertyValue::PublisherCount(count) => {
110+
writer.write_u8(PriceFeedProperty::PublisherCount as u8)?;
111+
write_option_u16::<BO>(&mut writer, *count)?;
112+
}
113+
PayloadPropertyValue::Exponent(exponent) => {
114+
writer.write_u8(PriceFeedProperty::Exponent as u8)?;
115+
writer.write_i16::<BO>(*exponent)?;
116+
}
99117
}
100118
}
101119
}
@@ -134,6 +152,10 @@ impl PayloadData {
134152
PayloadPropertyValue::BestBidPrice(read_option_price::<BO>(&mut reader)?)
135153
} else if property == PriceFeedProperty::BestAskPrice as u8 {
136154
PayloadPropertyValue::BestAskPrice(read_option_price::<BO>(&mut reader)?)
155+
} else if property == PriceFeedProperty::PublisherCount as u8 {
156+
PayloadPropertyValue::PublisherCount(read_option_u16::<BO>(&mut reader)?)
157+
} else if property == PriceFeedProperty::Exponent as u8 {
158+
PayloadPropertyValue::Exponent(reader.read_i16::<BO>()?)
137159
} else {
138160
bail!("unknown property");
139161
};
@@ -161,6 +183,18 @@ fn read_option_price<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Op
161183
Ok(value.map(Price))
162184
}
163185

186+
fn write_option_u16<BO: ByteOrder>(
187+
mut writer: impl Write,
188+
value: Option<u16>,
189+
) -> std::io::Result<()> {
190+
writer.write_u16::<BO>(value.unwrap_or(0))
191+
}
192+
193+
fn read_option_u16<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<u16>> {
194+
let value = reader.read_u16::<BO>()?;
195+
Ok(Some(value))
196+
}
197+
164198
pub const BINARY_UPDATE_FORMAT_MAGIC: u32 = 1937213467;
165199

166200
pub const PARSED_FORMAT_MAGIC: u32 = 2584795844;

0 commit comments

Comments
 (0)