pyth_lazer_sdk/
signature.rs1use {
2 anchor_lang::{prelude::Clock, AccountDeserialize},
3 bytemuck::{cast_slice, checked::try_cast_slice, Pod, Zeroable},
4 byteorder::{ByteOrder, LE},
5 solana_program::{
6 account_info::AccountInfo,
7 ed25519_program,
8 program_error::ProgramError,
9 pubkey::PUBKEY_BYTES,
10 sysvar::{self, Sysvar},
11 },
12 thiserror::Error,
13};
14
15const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2;
16
17const SIGNATURE_LEN: u16 = 64;
18const PUBKEY_LEN: u16 = 32;
19const MAGIC_LEN: u16 = 4;
20const MESSAGE_SIZE_LEN: u16 = 2;
21
22#[derive(Debug, Clone, Copy, Zeroable, Pod)]
29#[repr(C)]
30pub struct Ed25519SignatureOffsets {
31 pub signature_offset: u16,
33 pub signature_instruction_index: u16,
35 pub public_key_offset: u16,
37 pub public_key_instruction_index: u16,
39 pub message_data_offset: u16,
41 pub message_data_size: u16,
43 pub message_instruction_index: u16,
45}
46
47pub fn signature_offsets(
55 instruction_data: &[u8],
56 instruction_index: u16,
57 starting_offset: u16,
58) -> Ed25519SignatureOffsets {
59 let signature_offset = starting_offset + MAGIC_LEN;
60 let public_key_offset = signature_offset + SIGNATURE_LEN;
61 let message_data_size_offset = public_key_offset + PUBKEY_LEN;
62 let message_data_offset = message_data_size_offset + MESSAGE_SIZE_LEN;
63 let message_data_size = LE::read_u16(
64 &instruction_data[message_data_size_offset.into()..message_data_offset.into()],
65 );
66 Ed25519SignatureOffsets {
67 signature_offset,
68 signature_instruction_index: instruction_index,
69 public_key_offset,
70 public_key_instruction_index: instruction_index,
71 message_data_offset,
72 message_data_size,
73 message_instruction_index: instruction_index,
74 }
75}
76
77pub fn ed25519_program_args(signatures: &[Ed25519SignatureOffsets]) -> Vec<u8> {
79 let padding = 0u8;
80 let mut signature_args = vec![
81 signatures.len().try_into().expect("too many signatures"),
82 padding,
83 ];
84 signature_args.extend_from_slice(cast_slice(signatures));
85 signature_args
86}
87
88#[derive(Debug, Clone, Copy)]
90pub struct VerifiedMessage<'a> {
91 pub public_key: &'a [u8],
93 pub payload: &'a [u8],
95}
96
97#[derive(Debug, Error)]
98pub enum SignatureVerificationError {
99 #[error("ed25519 instruction must precede current instruction")]
100 Ed25519InstructionMustPrecedeCurrentInstruction,
101 #[error("load instruction at failed")]
102 LoadInstructionAtFailed(#[source] ProgramError),
103 #[error("load current index failed")]
104 LoadCurrentIndexFailed(#[source] ProgramError),
105 #[error("load current index failed")]
106 ClockGetFailed(#[source] ProgramError),
107 #[error("invalid ed25519 instruction program")]
108 InvalidEd25519InstructionProgramId,
109 #[error("invalid ed25519 instruction data length")]
110 InvalidEd25519InstructionDataLength,
111 #[error("invalid signature index")]
112 InvalidSignatureIndex,
113 #[error("invalid signature offset")]
114 InvalidSignatureOffset,
115 #[error("invalid public key offset")]
116 InvalidPublicKeyOffset,
117 #[error("invalid message offset")]
118 InvalidMessageOffset,
119 #[error("invalid message data size")]
120 InvalidMessageDataSize,
121 #[error("invalid instruction index")]
122 InvalidInstructionIndex,
123 #[error("message offset overflow")]
124 MessageOffsetOverflow,
125 #[error("format magic mismatch")]
126 FormatMagicMismatch,
127 #[error("invalid storage account id")]
128 InvalidStorageAccountId,
129 #[error("invalid storage data")]
130 InvalidStorageData,
131 #[error("not a trusted signer")]
132 NotTrustedSigner,
133}
134
135impl From<SignatureVerificationError> for ProgramError {
136 fn from(value: SignatureVerificationError) -> Self {
137 match value {
138 SignatureVerificationError::LoadInstructionAtFailed(e)
139 | SignatureVerificationError::ClockGetFailed(e)
140 | SignatureVerificationError::LoadCurrentIndexFailed(e) => e,
141 SignatureVerificationError::InvalidStorageData => ProgramError::InvalidAccountData,
142 SignatureVerificationError::NotTrustedSigner => ProgramError::MissingRequiredSignature,
143 _ => ProgramError::InvalidInstructionData,
144 }
145 }
146}
147
148pub fn verify_message<'a>(
158 pyth_storage_account: &AccountInfo,
159 instruction_sysvar: &AccountInfo,
160 message_data: &'a [u8],
161 ed25519_instruction_index: u16,
162 signature_index: u8,
163 message_offset: u16,
164) -> Result<VerifiedMessage<'a>, SignatureVerificationError> {
165 if pyth_storage_account.key != &pyth_lazer_solana_contract::storage::ID {
166 return Err(SignatureVerificationError::InvalidStorageAccountId);
167 }
168 let storage = {
169 let storage_data = pyth_storage_account.data.borrow();
170 let mut storage_data: &[u8] = *storage_data;
171 pyth_lazer_solana_contract::Storage::try_deserialize(&mut storage_data)
172 .map_err(|_| SignatureVerificationError::InvalidStorageData)?
173 };
174
175 const SOLANA_FORMAT_MAGIC_LE: u32 = 2182742457;
176
177 let self_instruction_index =
178 sysvar::instructions::load_current_index_checked(instruction_sysvar)
179 .map_err(SignatureVerificationError::LoadCurrentIndexFailed)?;
180
181 if ed25519_instruction_index >= self_instruction_index {
182 return Err(SignatureVerificationError::Ed25519InstructionMustPrecedeCurrentInstruction);
183 }
184
185 let instruction = sysvar::instructions::load_instruction_at_checked(
186 ed25519_instruction_index.into(),
187 instruction_sysvar,
188 )
189 .map_err(SignatureVerificationError::LoadInstructionAtFailed)?;
190
191 if instruction.program_id != ed25519_program::ID {
192 return Err(SignatureVerificationError::InvalidEd25519InstructionProgramId);
193 }
194 if instruction.data.len() < ED25519_PROGRAM_INPUT_HEADER_LEN {
195 return Err(SignatureVerificationError::InvalidEd25519InstructionDataLength);
196 }
197
198 let num_signatures = instruction.data[0];
199 if signature_index >= num_signatures {
200 return Err(SignatureVerificationError::InvalidSignatureIndex);
201 }
202 let args: &[Ed25519SignatureOffsets] =
203 try_cast_slice(&instruction.data[ED25519_PROGRAM_INPUT_HEADER_LEN..])
204 .map_err(|_| SignatureVerificationError::InvalidEd25519InstructionDataLength)?;
205
206 let args_len = args
207 .len()
208 .try_into()
209 .map_err(|_| SignatureVerificationError::InvalidEd25519InstructionDataLength)?;
210 if signature_index >= args_len {
211 return Err(SignatureVerificationError::InvalidSignatureIndex);
212 }
213 let offsets = &args[usize::from(signature_index)];
214
215 let expected_signature_offset = message_offset
216 .checked_add(MAGIC_LEN)
217 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
218 if offsets.signature_offset != expected_signature_offset {
219 return Err(SignatureVerificationError::InvalidSignatureOffset);
220 }
221
222 let magic = LE::read_u32(&message_data[..MAGIC_LEN.into()]);
223 if magic != SOLANA_FORMAT_MAGIC_LE {
224 return Err(SignatureVerificationError::FormatMagicMismatch);
225 }
226
227 let expected_public_key_offset = expected_signature_offset
228 .checked_add(SIGNATURE_LEN)
229 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
230 if offsets.public_key_offset != expected_public_key_offset {
231 return Err(SignatureVerificationError::InvalidPublicKeyOffset);
232 }
233
234 let expected_message_size_offset = expected_public_key_offset
235 .checked_add(PUBKEY_LEN)
236 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
237
238 let expected_message_data_offset = expected_message_size_offset
239 .checked_add(MESSAGE_SIZE_LEN)
240 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
241 if offsets.message_data_offset != expected_message_data_offset {
242 return Err(SignatureVerificationError::InvalidMessageOffset);
243 }
244
245 let expected_message_size = {
246 let start = usize::from(
247 expected_message_size_offset
248 .checked_sub(message_offset)
249 .unwrap(),
250 );
251 let end = usize::from(
252 expected_message_data_offset
253 .checked_sub(message_offset)
254 .unwrap(),
255 );
256 LE::read_u16(&message_data[start..end])
257 };
258 if offsets.message_data_size != expected_message_size {
259 return Err(SignatureVerificationError::InvalidMessageDataSize);
260 }
261 if offsets.signature_instruction_index != self_instruction_index
262 || offsets.public_key_instruction_index != self_instruction_index
263 || offsets.message_instruction_index != self_instruction_index
264 {
265 return Err(SignatureVerificationError::InvalidInstructionIndex);
266 }
267
268 let public_key = {
269 let start = usize::from(
270 expected_public_key_offset
271 .checked_sub(message_offset)
272 .unwrap(),
273 );
274 let end = start
275 .checked_add(PUBKEY_BYTES)
276 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
277 &message_data[start..end]
278 };
279 let now = Clock::get()
280 .map_err(SignatureVerificationError::ClockGetFailed)?
281 .unix_timestamp;
282 if !storage
283 .initialized_trusted_signers()
284 .iter()
285 .any(|s| s.pubkey.as_ref() == public_key && s.expires_at > now)
286 {
287 return Err(SignatureVerificationError::NotTrustedSigner);
288 }
289
290 let payload = {
291 let start = usize::from(
292 expected_message_data_offset
293 .checked_sub(message_offset)
294 .unwrap(),
295 );
296 let end = start
297 .checked_add(expected_message_size.into())
298 .ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
299 &message_data[start..end]
300 };
301
302 Ok(VerifiedMessage {
303 public_key,
304 payload,
305 })
306}