Skip to content

Commit 41e7906

Browse files
committed
fix(lazer): extract message data from instructions sysvar when verifying signature
1 parent 7c121bb commit 41e7906

File tree

4 files changed

+54
-37
lines changed

4 files changed

+54
-37
lines changed

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,13 @@ pub mod pyth_lazer_solana_contract {
186186
/// Verifies a ed25519 signature on Solana by checking that the transaction contains
187187
/// a correct call to the built-in `ed25519_program`.
188188
///
189-
/// - `message_data` is the signed message that is being verified.
190189
/// - `ed25519_instruction_index` is the index of the `ed25519_program` instruction
191190
/// within the transaction. This instruction must precede the current instruction.
192191
/// - `signature_index` is the index of the signature within the inputs to the `ed25519_program`.
193-
/// - `message_offset` is the offset of the signed message within the
194-
/// input data for the current instruction.
195192
pub fn verify_message(
196193
ctx: Context<VerifyMessage>,
197-
message_data: Vec<u8>,
198194
ed25519_instruction_index: u16,
199195
signature_index: u8,
200-
message_offset: u16,
201196
) -> Result<VerifiedMessage> {
202197
system_program::transfer(
203198
CpiContext::new(
@@ -213,10 +208,8 @@ pub mod pyth_lazer_solana_contract {
213208
signature::verify_message(
214209
&ctx.accounts.storage,
215210
&ctx.accounts.instructions_sysvar,
216-
&message_data,
217211
ed25519_instruction_index,
218212
signature_index,
219-
message_offset,
220213
)
221214
.map_err(|err| {
222215
msg!("signature verification error: {:?}", err);

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

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,14 @@ impl From<SignatureVerificationError> for anchor_lang::error::Error {
151151
/// Verifies a ed25519 signature on Solana by checking that the transaction contains
152152
/// a correct call to the built-in `ed25519_program`.
153153
///
154-
/// - `message_data` is the signed message that is being verified.
155154
/// - `ed25519_instruction_index` is the index of the `ed25519_program` instruction
156155
/// within the transaction. This instruction must precede the current instruction.
157156
/// - `signature_index` is the index of the signature within the inputs to the `ed25519_program`.
158-
/// - `message_offset` is the offset of the signed message within the
159-
/// input data for the current instruction.
160157
pub fn verify_message(
161158
storage: &Storage,
162159
instructions_sysvar: &AccountInfo,
163-
message_data: &[u8],
164160
ed25519_instruction_index: u16,
165161
signature_index: u8,
166-
message_offset: u16,
167162
) -> Result<VerifiedMessage, SignatureVerificationError> {
168163
const SOLANA_FORMAT_MAGIC_LE: u32 = 2182742457;
169164

@@ -175,25 +170,25 @@ pub fn verify_message(
175170
return Err(SignatureVerificationError::Ed25519InstructionMustPrecedeCurrentInstruction);
176171
}
177172

178-
let instruction = sysvar::instructions::load_instruction_at_checked(
173+
let ed25519_instruction = sysvar::instructions::load_instruction_at_checked(
179174
ed25519_instruction_index.into(),
180175
instructions_sysvar,
181176
)
182177
.map_err(SignatureVerificationError::LoadInstructionAtFailed)?;
183178

184-
if instruction.program_id != ed25519_program::ID {
179+
if ed25519_instruction.program_id != ed25519_program::ID {
185180
return Err(SignatureVerificationError::InvalidEd25519InstructionProgramId);
186181
}
187-
if instruction.data.len() < ED25519_PROGRAM_INPUT_HEADER_LEN {
182+
if ed25519_instruction.data.len() < ED25519_PROGRAM_INPUT_HEADER_LEN {
188183
return Err(SignatureVerificationError::InvalidEd25519InstructionDataLength);
189184
}
190185

191-
let num_signatures = instruction.data[0];
186+
let num_signatures = ed25519_instruction.data[0];
192187
if signature_index >= num_signatures {
193188
return Err(SignatureVerificationError::InvalidSignatureIndex);
194189
}
195190
let args: &[Ed25519SignatureOffsets] =
196-
try_cast_slice(&instruction.data[ED25519_PROGRAM_INPUT_HEADER_LEN..])
191+
try_cast_slice(&ed25519_instruction.data[ED25519_PROGRAM_INPUT_HEADER_LEN..])
197192
.map_err(|_| SignatureVerificationError::InvalidEd25519InstructionDataLength)?;
198193

199194
let args_len = args
@@ -205,19 +200,41 @@ pub fn verify_message(
205200
}
206201
let offsets = &args[usize::from(signature_index)];
207202

208-
let expected_signature_offset = message_offset
209-
.checked_add(MAGIC_LEN)
210-
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
211-
if offsets.signature_offset != expected_signature_offset {
212-
return Err(SignatureVerificationError::InvalidSignatureOffset);
203+
if offsets.signature_instruction_index != offsets.message_instruction_index
204+
|| offsets.public_key_instruction_index != offsets.message_instruction_index
205+
{
206+
return Err(SignatureVerificationError::InvalidInstructionIndex);
213207
}
214208

209+
if ed25519_instruction_index >= offsets.message_instruction_index {
210+
return Err(SignatureVerificationError::Ed25519InstructionMustPrecedeCurrentInstruction);
211+
}
212+
let message_instruction = sysvar::instructions::load_instruction_at_checked(
213+
offsets.message_instruction_index.into(),
214+
instructions_sysvar,
215+
)
216+
.map_err(SignatureVerificationError::LoadInstructionAtFailed)?;
217+
218+
let message_offset = offsets
219+
.signature_offset
220+
.checked_sub(MAGIC_LEN)
221+
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
222+
let message_end_offset = offsets
223+
.message_data_offset
224+
.checked_add(offsets.message_data_size)
225+
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
226+
let message_data = message_instruction
227+
.data
228+
.get(usize::from(message_offset)..usize::from(message_end_offset))
229+
.ok_or(SignatureVerificationError::InvalidMessageOffset)?;
230+
215231
let magic = LE::read_u32(&message_data[..MAGIC_LEN.into()]);
216232
if magic != SOLANA_FORMAT_MAGIC_LE {
217233
return Err(SignatureVerificationError::FormatMagicMismatch);
218234
}
219235

220-
let expected_public_key_offset = expected_signature_offset
236+
let expected_public_key_offset = offsets
237+
.signature_offset
221238
.checked_add(SIGNATURE_LEN)
222239
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
223240
if offsets.public_key_offset != expected_public_key_offset {
@@ -251,12 +268,6 @@ pub fn verify_message(
251268
if offsets.message_data_size != expected_message_size {
252269
return Err(SignatureVerificationError::InvalidMessageDataSize);
253270
}
254-
if offsets.signature_instruction_index != self_instruction_index
255-
|| offsets.public_key_instruction_index != self_instruction_index
256-
|| offsets.message_instruction_index != self_instruction_index
257-
{
258-
return Err(SignatureVerificationError::InvalidInstructionIndex);
259-
}
260271

261272
let public_key = {
262273
let start = usize::from(
Binary file not shown.

lazer/contracts/solana/programs/pyth-lazer-solana-contract/tests/test1.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ use {
77
ed25519_program,
88
hash::Hash,
99
instruction::{Instruction, InstructionError},
10+
pubkey,
1011
pubkey::{Pubkey, PUBKEY_BYTES},
1112
signature::Keypair,
1213
signer::Signer,
1314
system_instruction, system_program, system_transaction, sysvar,
1415
transaction::{Transaction, TransactionError},
1516
},
16-
std::env,
17+
std::{env, path::Path},
1718
};
1819

20+
const NOOP_PROGRAM_ID: Pubkey = pubkey!("HssDLpcBSwJQLJG7unwGCuJpBEdhdvHitoqqn2nLoN6");
21+
1922
fn program_test() -> ProgramTest {
2023
if env::var("SBF_OUT_DIR").is_err() {
2124
env::set_var(
@@ -26,12 +29,23 @@ fn program_test() -> ProgramTest {
2629
),
2730
);
2831
}
32+
std::fs::copy(
33+
dbg!(Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
34+
.join("tests/solana_noop_program.so")),
35+
dbg!(format!(
36+
"{}/../../../../target/sbf-solana-solana/release/solana_noop_program.so",
37+
env::var("CARGO_MANIFEST_DIR").unwrap()
38+
)),
39+
)
40+
.unwrap();
2941
println!("if add_program fails, run `cargo build-sbf` first.");
30-
ProgramTest::new(
42+
let mut program_test = ProgramTest::new(
3143
"pyth_lazer_solana_contract",
3244
pyth_lazer_solana_contract::ID,
3345
None,
34-
)
46+
);
47+
program_test.add_program("solana_noop_program", NOOP_PROGRAM_ID, None);
48+
program_test
3549
}
3650

3751
struct Setup {
@@ -104,10 +118,10 @@ impl Setup {
104118

105119
async fn verify_message(&mut self, message: &[u8], treasury: Pubkey) {
106120
// Instruction #0 will be ed25519 instruction;
107-
// Instruction #1 will be our contract instruction.
121+
// Instruction #1 will be the message receiver's instruction.
108122
let instruction_index = 1;
109-
// 8 bytes for Anchor header, 4 bytes for Vec length.
110-
let message_offset = 12;
123+
// The whole instruction data is the lazer message.
124+
let message_offset = 0;
111125
let ed25519_args = dbg!(pyth_lazer_solana_contract::Ed25519SignatureOffsets::new(
112126
message,
113127
instruction_index,
@@ -128,13 +142,12 @@ impl Setup {
128142
&ed25519_program_args(&[ed25519_args]),
129143
vec![],
130144
),
145+
Instruction::new_with_bytes(NOOP_PROGRAM_ID, message, vec![]),
131146
Instruction::new_with_bytes(
132147
pyth_lazer_solana_contract::ID,
133148
&pyth_lazer_solana_contract::instruction::VerifyMessage {
134-
message_data: message.to_vec(),
135149
ed25519_instruction_index: 0,
136150
signature_index: 0,
137-
message_offset,
138151
}
139152
.data(),
140153
vec![

0 commit comments

Comments
 (0)