solana_sdk/
secp256k1_instruction.rs

1//! Instructions for the [secp256k1 native program][np].
2//!
3//! [np]: https://docs.solanalabs.com/runtime/programs#secp256k1-program
4//!
5//! _This module provides low-level cryptographic building blocks that must be
6//! used carefully to ensure proper security. Read this documentation and
7//! accompanying links thoroughly._
8//!
9//! The secp26k1 native program peforms flexible verification of [secp256k1]
10//! ECDSA signatures, as used by Ethereum. It can verify up to 255 signatures on
11//! up to 255 messages, with those signatures, messages, and their public keys
12//! arbitrarily distributed across the instruction data of any instructions in
13//! the same transaction as the secp256k1 instruction.
14//!
15//! The secp256k1 native program ID is located in the [`secp256k1_program`] module.
16//!
17//! The instruction is designed for Ethereum interoperability, but may be useful
18//! for other purposes. It operates on Ethereum addresses, which are [`keccak`]
19//! hashes of secp256k1 public keys, and internally is implemented using the
20//! secp256k1 key recovery algorithm. Ethereum address can be created for
21//! secp256k1 public keys with the [`construct_eth_pubkey`] function.
22//!
23//! [`keccak`]: crate::keccak
24//!
25//! This instruction does not directly allow for key recovery as in Ethereum's
26//! [`ecrecover`] precompile. For that Solana provides the [`secp256k1_recover`]
27//! syscall.
28//!
29//! [secp256k1]: https://en.bitcoin.it/wiki/Secp256k1
30//! [`secp256k1_program`]: solana_program::secp256k1_program
31//! [`secp256k1_recover`]: solana_program::secp256k1_recover
32//! [`ecrecover`]: https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions
33//!
34//! Use cases for the secp256k1 instruction include:
35//!
36//! - Verifying Ethereum transaction signatures.
37//! - Verifying Ethereum [EIP-712] signatures.
38//! - Verifying arbitrary secp256k1 signatures.
39//! - Signing a single message with multiple signatures.
40//!
41//! [EIP-712]: https://eips.ethereum.org/EIPS/eip-712
42//!
43//! The [`new_secp256k1_instruction`] function is suitable for building a
44//! secp256k1 program instruction for basic use cases were a single message must
45//! be signed by a known secret key. For other uses cases, including many
46//! Ethereum-integration use cases, construction of the secp256k1 instruction
47//! must be done manually.
48//!
49//! # How to use this program
50//!
51//! Transactions that uses the secp256k1 native program will typically include
52//! at least two instructions: one for the secp256k1 program to verify the
53//! signatures, and one for a custom program that will check that the secp256k1
54//! instruction data matches what the program expects (using
55//! [`load_instruction_at_checked`] or [`get_instruction_relative`]). The
56//! signatures, messages, and Ethereum addresses being verified may reside in the
57//! instruction data of either of these instructions, or in the instruction data
58//! of one or more additional instructions, as long as those instructions are in
59//! the same transaction.
60//!
61//! [`load_instruction_at_checked`]: crate::sysvar::instructions::load_instruction_at_checked
62//! [`get_instruction_relative`]: crate::sysvar::instructions::get_instruction_relative
63//!
64//! Correct use of this program involves multiple steps, in client code and
65//! program code:
66//!
67//! - In the client:
68//!   - Sign the [`keccak`]-hashed messages with a secp256k1 ECDSA library,
69//!     like the [`libsecp256k1`] crate.
70//!   - Build any custom instruction data that contain signature, message, or
71//!     Ethereum address data that will be used by the secp256k1 instruction.
72//!   - Build the secp256k1 program instruction data, specifying the number of
73//!     signatures to verify, the instruction indexes within the transaction,
74//!     and offsets within those instruction's data, where the signatures,
75//!     messages, and Ethereum addresses are located.
76//!   - Build the custom instruction for the program that will check the results
77//!     of the secp256k1 native program.
78//!   - Package all instructions into a single transaction and submit them.
79//! - In the program:
80//!   - Load the secp256k1 instruction data with
81//!     [`load_instruction_at_checked`]. or [`get_instruction_relative`].
82//!   - Check that the secp256k1 program ID is equal to
83//!     [`secp256k1_program::ID`], so that the signature verification cannot be
84//!     faked with a malicious program.
85//!   - Check that the public keys and messages are the expected values per
86//!     the program's requirements.
87//!
88//! [`secp256k1_program::ID`]: crate::secp256k1_program::ID
89//!
90//! The signature, message, or Ethereum addresses may reside in the secp256k1
91//! instruction data itself as additional data, their bytes following the bytes
92//! of the protocol required by the secp256k1 instruction to locate the
93//! signature, message, and Ethereum address data. This is the technique used by
94//! `new_secp256k1_instruction` for simple signature verification.
95//!
96//! The `solana_sdk` crate provides few APIs for building the instructions and
97//! transactions necessary for properly using the secp256k1 native program.
98//! Many steps must be done manually.
99//!
100//! The `solana_program` crate provides no APIs to assist in interpreting
101//! the the secp256k1 instruction data. It must be done manually.
102//!
103//! The secp256k1 program is implemented with the [`libsecp256k1`] crate,
104//! which clients may also want to use.
105//!
106//! [`libsecp256k1`]: https://docs.rs/libsecp256k1/latest/libsecp256k1
107//!
108//! # Layout and interpretation of the secp256k1 instruction data
109//!
110//! The secp256k1 instruction data contains:
111//!
112//! - 1 byte indicating the number of signatures to verify, 0 - 255,
113//! - A number of _signature offset_ structures that indicate where in the
114//!   transaction to locate each signature, message, and Ethereum address.
115//! - 0 or more bytes of arbitrary data, which may contain signatures,
116//!   messages or Ethereum addresses.
117//!
118//! The signature offset structure is defined by [`SecpSignatureOffsets`],
119//! and can be serialized to the correct format with [`bincode::serialize_into`].
120//! Note that the bincode format may not be stable,
121//! and callers should ensure they use the same version of `bincode` as the Solana SDK.
122//! This data structure is not provided to Solana programs,
123//! which are expected to interpret the signature offsets manually.
124//!
125//! [`bincode::serialize_into`]: https://docs.rs/bincode/1.3.3/bincode/fn.serialize_into.html
126//!
127//! The serialized signature offset structure has the following 11-byte layout,
128//! with data types in little-endian encoding.
129//!
130//! | index  | bytes | type  | description |
131//! |--------|-------|-------|-------------|
132//! | 0      | 2     | `u16` | `signature_offset` - offset to 64-byte signature plus 1-byte recovery ID. |
133//! | 2      | 1     | `u8`  | `signature_offset_instruction_index` - within the transaction, the index of the transaction whose instruction data contains the signature. |
134//! | 3      | 2     | `u16` | `eth_address_offset` - offset to 20-byte Ethereum address. |
135//! | 5      | 1     | `u8`  | `eth_address_instruction_index` - within the transaction, the index of the instruction whose instruction data contains the Ethereum address. |
136//! | 6      | 2     | `u16` | `message_data_offset` - Offset to start of message data. |
137//! | 8      | 2     | `u16` | `message_data_size` - Size of message data in bytes. |
138//! | 10     | 1     | `u8`  | `message_instruction_index` - Within the transaction, the index of the instruction whose instruction data contains the message data. |
139//!
140//! # Signature malleability
141//!
142//! With the ECDSA signature algorithm it is possible for any party, given a
143//! valid signature of some message, to create a second signature that is
144//! equally valid. This is known as _signature malleability_. In many cases this
145//! is not a concern, but in cases where applications rely on signatures to have
146//! a unique representation this can be the source of bugs, potentially with
147//! security implications.
148//!
149//! **The solana `secp256k1_recover` function does not prevent signature
150//! malleability**. This is in contrast to the Bitcoin secp256k1 library, which
151//! does prevent malleability by default. Solana accepts signatures with `S`
152//! values that are either in the _high order_ or in the _low order_, and it
153//! is trivial to produce one from the other.
154//!
155//! For more complete documentation of the subject, and techniques to prevent
156//! malleability, see the documentation for the [`secp256k1_recover`] syscall.
157//!
158//! # Additional security considerations
159//!
160//! Most programs will want to be conservative about the layout of the secp256k1 instruction
161//! to prevent unforeseen bugs. The following checks may be desirable:
162//!
163//! - That there are exactly the expected number of signatures.
164//! - That the three indexes, `signature_offset_instruction_index`,
165//!   `eth_address_instruction_index`, and `message_instruction_index` are as
166//!   expected, placing the signature, message and Ethereum address in the
167//!   expected instruction.
168//!
169//! Loading the secp256k1 instruction data within a program requires access to
170//! the [instructions sysvar][is], which must be passed to the program by its
171//! caller. Programs must verify the ID of this program to avoid calling an
172//! imposter program. This does not need to be done manually though, as long as
173//! it is only used through the [`load_instruction_at_checked`] or
174//! [`get_instruction_relative`] functions. Both of these functions check their
175//! sysvar argument to ensure it is the known instruction sysvar.
176//!
177//! [is]: crate::sysvar::instructions
178//!
179//! Programs should _always_ verify that the secp256k1 program ID loaded through
180//! the instructions sysvar has the same value as in the [`secp256k1_program`]
181//! module. Again this prevents imposter programs.
182//!
183//! [`secp256k1_program`]: crate::secp256k1_program
184//!
185//! # Errors
186//!
187//! The transaction will fail if any of the following are true:
188//!
189//! - Any signature was not created by the secret key corresponding to the
190//!   specified public key.
191//! - Any signature is invalid.
192//! - Any signature is "overflowing", a non-standard condition.
193//! - The instruction data is empty.
194//! - The first byte of instruction data is equal to 0 (indicating no signatures),
195//!   but the instruction data's length is greater than 1.
196//! - The instruction data is not long enough to hold the number of signature
197//!   offsets specified in the first byte.
198//! - Any instruction indexes specified in the signature offsets are greater or
199//!   equal to the number of instructions in the transaction.
200//! - Any bounds specified in the signature offsets exceed the bounds of the
201//!   instruction data to which they are indexed.
202//!
203//! # Examples
204//!
205//! Both of the following examples make use of the following module definition
206//! to parse the secp256k1 instruction data from within a Solana program.
207//!
208//! ```no_run
209//! mod secp256k1_defs {
210//!     use solana_program::program_error::ProgramError;
211//!     use std::iter::Iterator;
212//!
213//!     pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
214//!     pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
215//!     pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
216//!
217//!     /// The structure encoded in the secp2256k1 instruction data.
218//!     pub struct SecpSignatureOffsets {
219//!         pub signature_offset: u16,
220//!         pub signature_instruction_index: u8,
221//!         pub eth_address_offset: u16,
222//!         pub eth_address_instruction_index: u8,
223//!         pub message_data_offset: u16,
224//!         pub message_data_size: u16,
225//!         pub message_instruction_index: u8,
226//!     }
227//!
228//!     pub fn iter_signature_offsets(
229//!        secp256k1_instr_data: &[u8],
230//!     ) -> Result<impl Iterator<Item = SecpSignatureOffsets> + '_, ProgramError> {
231//!         // First element is the number of `SecpSignatureOffsets`.
232//!         let num_structs = *secp256k1_instr_data
233//!             .get(0)
234//!             .ok_or(ProgramError::InvalidArgument)?;
235//!
236//!         let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize;
237//!         let all_structs_slice = secp256k1_instr_data
238//!             .get(1..all_structs_size + 1)
239//!             .ok_or(ProgramError::InvalidArgument)?;
240//!
241//!         fn decode_u16(chunk: &[u8], index: usize) -> u16 {
242//!             u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap())
243//!         }
244//!
245//!         Ok(all_structs_slice
246//!             .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
247//!             .map(|chunk| SecpSignatureOffsets {
248//!                 signature_offset: decode_u16(chunk, 0),
249//!                 signature_instruction_index: chunk[2],
250//!                 eth_address_offset: decode_u16(chunk, 3),
251//!                 eth_address_instruction_index: chunk[5],
252//!                 message_data_offset: decode_u16(chunk, 6),
253//!                 message_data_size: decode_u16(chunk, 8),
254//!                 message_instruction_index: chunk[10],
255//!             }))
256//!     }
257//! }
258//! ```
259//!
260//! ## Example: Signing and verifying with `new_secp256k1_instruction`
261//!
262//! This example demonstrates the simplest way to use the secp256k1 program, by
263//! calling [`new_secp256k1_instruction`] to sign a single message and build the
264//! corresponding secp256k1 instruction.
265//!
266//! This example has two components: a Solana program, and an RPC client that
267//! sends a transaction to call it. The RPC client will sign a single message,
268//! and the Solana program will introspect the secp256k1 instruction to verify
269//! that the signer matches a known authorized public key.
270//!
271//! The Solana program. Note that it uses `libsecp256k1` version 0.7.0 to parse
272//! the secp256k1 signature to prevent malleability.
273//!
274//! ```no_run
275//! # mod secp256k1_defs {
276//! #     use solana_program::program_error::ProgramError;
277//! #     use std::iter::Iterator;
278//! #
279//! #     pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
280//! #     pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
281//! #     pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
282//! #
283//! #     /// The structure encoded in the secp2256k1 instruction data.
284//! #     pub struct SecpSignatureOffsets {
285//! #         pub signature_offset: u16,
286//! #         pub signature_instruction_index: u8,
287//! #         pub eth_address_offset: u16,
288//! #         pub eth_address_instruction_index: u8,
289//! #         pub message_data_offset: u16,
290//! #         pub message_data_size: u16,
291//! #         pub message_instruction_index: u8,
292//! #     }
293//! #
294//! #     pub fn iter_signature_offsets(
295//! #        secp256k1_instr_data: &[u8],
296//! #     ) -> Result<impl Iterator<Item = SecpSignatureOffsets> + '_, ProgramError> {
297//! #         // First element is the number of `SecpSignatureOffsets`.
298//! #         let num_structs = *secp256k1_instr_data
299//! #             .get(0)
300//! #             .ok_or(ProgramError::InvalidArgument)?;
301//! #
302//! #         let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize;
303//! #         let all_structs_slice = secp256k1_instr_data
304//! #             .get(1..all_structs_size + 1)
305//! #             .ok_or(ProgramError::InvalidArgument)?;
306//! #
307//! #         fn decode_u16(chunk: &[u8], index: usize) -> u16 {
308//! #             u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap())
309//! #         }
310//! #
311//! #         Ok(all_structs_slice
312//! #             .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
313//! #             .map(|chunk| SecpSignatureOffsets {
314//! #                 signature_offset: decode_u16(chunk, 0),
315//! #                 signature_instruction_index: chunk[2],
316//! #                 eth_address_offset: decode_u16(chunk, 3),
317//! #                 eth_address_instruction_index: chunk[5],
318//! #                 message_data_offset: decode_u16(chunk, 6),
319//! #                 message_data_size: decode_u16(chunk, 8),
320//! #                 message_instruction_index: chunk[10],
321//! #             }))
322//! #     }
323//! # }
324//! use solana_program::{
325//!     account_info::{next_account_info, AccountInfo},
326//!     entrypoint::ProgramResult,
327//!     msg,
328//!     program_error::ProgramError,
329//!     secp256k1_program,
330//!     sysvar,
331//! };
332//!
333//! /// An Ethereum address corresponding to a secp256k1 secret key that is
334//! /// authorized to sign our messages.
335//! const AUTHORIZED_ETH_ADDRESS: [u8; 20] = [
336//!     0x18, 0x8a, 0x5c, 0xf2, 0x3b, 0x0e, 0xff, 0xe9, 0xa8, 0xe1, 0x42, 0x64, 0x5b, 0x82, 0x2f, 0x3a,
337//!     0x6b, 0x8b, 0x52, 0x35,
338//! ];
339//!
340//! /// Check the secp256k1 instruction to ensure it was signed by
341//! /// `AUTHORIZED_ETH_ADDRESS`s key.
342//! ///
343//! /// `accounts` is the slice of all accounts passed to the program
344//! /// entrypoint. The only account it should contain is the instructions sysvar.
345//! fn demo_secp256k1_verify_basic(
346//!    accounts: &[AccountInfo],
347//! ) -> ProgramResult {
348//!     let account_info_iter = &mut accounts.iter();
349//!
350//!     // The instructions sysvar gives access to the instructions in the transaction.
351//!     let instructions_sysvar_account = next_account_info(account_info_iter)?;
352//!     assert!(sysvar::instructions::check_id(
353//!         instructions_sysvar_account.key
354//!     ));
355//!
356//!     // Load the secp256k1 instruction.
357//!     // `new_secp256k1_instruction` generates an instruction that must be at index 0.
358//!     let secp256k1_instr =
359//!         sysvar::instructions::load_instruction_at_checked(0, instructions_sysvar_account)?;
360//!
361//!     // Verify it is a secp256k1 instruction.
362//!     // This is security-critical - what if the transaction uses an imposter secp256k1 program?
363//!     assert!(secp256k1_program::check_id(&secp256k1_instr.program_id));
364//!
365//!     // There must be at least one byte. This is also verified by the runtime,
366//!     // and doesn't strictly need to be checked.
367//!     assert!(secp256k1_instr.data.len() > 1);
368//!
369//!     let num_signatures = secp256k1_instr.data[0];
370//!     // `new_secp256k1_instruction` generates an instruction that contains one signature.
371//!     assert_eq!(1, num_signatures);
372//!
373//!     // Load the first and only set of signature offsets.
374//!     let offsets: secp256k1_defs::SecpSignatureOffsets =
375//!         secp256k1_defs::iter_signature_offsets(&secp256k1_instr.data)?
376//!             .next()
377//!             .ok_or(ProgramError::InvalidArgument)?;
378//!
379//!     // `new_secp256k1_instruction` generates an instruction that only uses instruction index 0.
380//!     assert_eq!(0, offsets.signature_instruction_index);
381//!     assert_eq!(0, offsets.eth_address_instruction_index);
382//!     assert_eq!(0, offsets.message_instruction_index);
383//!
384//!     // Reject high-s value signatures to prevent malleability.
385//!     // Solana does not do this itself.
386//!     // This may or may not be necessary depending on use case.
387//!     {
388//!         let signature = &secp256k1_instr.data[offsets.signature_offset as usize
389//!             ..offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE];
390//!         let signature = libsecp256k1::Signature::parse_standard_slice(signature)
391//!             .map_err(|_| ProgramError::InvalidArgument)?;
392//!
393//!         if signature.s.is_high() {
394//!             msg!("signature with high-s value");
395//!             return Err(ProgramError::InvalidArgument);
396//!         }
397//!     }
398//!
399//!     // There is likely at least one more verification step a real program needs
400//!     // to do here to ensure it trusts the secp256k1 instruction, e.g.:
401//!     //
402//!     // - verify the tx signer is authorized
403//!     // - verify the secp256k1 signer is authorized
404//!
405//!     // Here we are checking the secp256k1 pubkey against a known authorized pubkey.
406//!     let eth_address = &secp256k1_instr.data[offsets.eth_address_offset as usize
407//!         ..offsets.eth_address_offset as usize + secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE];
408//!
409//!     if eth_address != AUTHORIZED_ETH_ADDRESS {
410//!         return Err(ProgramError::InvalidArgument);
411//!     }
412//!
413//!     Ok(())
414//! }
415//! ```
416//!
417//! The client program:
418//!
419//! ```no_run
420//! # use solana_sdk::example_mocks::solana_rpc_client;
421//! use anyhow::Result;
422//! use solana_rpc_client::rpc_client::RpcClient;
423//! use solana_sdk::{
424//!     instruction::{AccountMeta, Instruction},
425//!     secp256k1_instruction,
426//!     signature::{Keypair, Signer},
427//!     sysvar,
428//!     transaction::Transaction,
429//! };
430//!
431//! fn demo_secp256k1_verify_basic(
432//!     payer_keypair: &Keypair,
433//!     secp256k1_secret_key: &libsecp256k1::SecretKey,
434//!     client: &RpcClient,
435//!     program_keypair: &Keypair,
436//! ) -> Result<()> {
437//!     // Internally to `new_secp256k1_instruction` and
438//!     // `secp256k_instruction::verify` (the secp256k1 program), this message is
439//!     // keccak-hashed before signing.
440//!     let msg = b"hello world";
441//!     let secp256k1_instr = secp256k1_instruction::new_secp256k1_instruction(&secp256k1_secret_key, msg);
442//!
443//!     let program_instr = Instruction::new_with_bytes(
444//!         program_keypair.pubkey(),
445//!         &[],
446//!         vec![
447//!             AccountMeta::new_readonly(sysvar::instructions::ID, false)
448//!         ],
449//!     );
450//!
451//!     let blockhash = client.get_latest_blockhash()?;
452//!     let tx = Transaction::new_signed_with_payer(
453//!         &[secp256k1_instr, program_instr],
454//!         Some(&payer_keypair.pubkey()),
455//!         &[payer_keypair],
456//!         blockhash,
457//!     );
458//!
459//!     client.send_and_confirm_transaction(&tx)?;
460//!
461//!     Ok(())
462//! }
463//! ```
464//!
465//! ## Example: Verifying multiple signatures in one instruction
466//!
467//! This examples demonstrates manually creating a secp256k1 instruction
468//! containing many signatures, and a Solana program that parses them all. This
469//! example on its own has no practical purpose. It simply demonstrates advanced
470//! use of the secp256k1 program.
471//!
472//! Recall that the secp256k1 program will accept signatures, messages, and
473//! Ethereum addresses that reside in any instruction contained in the same
474//! transaction. In the _previous_ example, the Solana program asserted that all
475//! signatures, messages, and addresses were stored in the instruction at 0. In
476//! this next example the Solana program supports signatures, messages, and
477//! addresses stored in any instruction. For simplicity the client still only
478//! stores signatures, messages, and addresses in a single instruction, the
479//! secp256k1 instruction. The code for storing this data across multiple
480//! instructions would be complex, and may not be necessary in practice.
481//!
482//! This example has two components: a Solana program, and an RPC client that
483//! sends a transaction to call it.
484//!
485//! The Solana program:
486//!
487//! ```no_run
488//! # mod secp256k1_defs {
489//! #     use solana_program::program_error::ProgramError;
490//! #     use std::iter::Iterator;
491//! #
492//! #     pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
493//! #     pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
494//! #     pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
495//! #
496//! #     /// The structure encoded in the secp2256k1 instruction data.
497//! #     pub struct SecpSignatureOffsets {
498//! #         pub signature_offset: u16,
499//! #         pub signature_instruction_index: u8,
500//! #         pub eth_address_offset: u16,
501//! #         pub eth_address_instruction_index: u8,
502//! #         pub message_data_offset: u16,
503//! #         pub message_data_size: u16,
504//! #         pub message_instruction_index: u8,
505//! #     }
506//! #
507//! #     pub fn iter_signature_offsets(
508//! #        secp256k1_instr_data: &[u8],
509//! #     ) -> Result<impl Iterator<Item = SecpSignatureOffsets> + '_, ProgramError> {
510//! #         // First element is the number of `SecpSignatureOffsets`.
511//! #         let num_structs = *secp256k1_instr_data
512//! #             .get(0)
513//! #             .ok_or(ProgramError::InvalidArgument)?;
514//! #
515//! #         let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize;
516//! #         let all_structs_slice = secp256k1_instr_data
517//! #             .get(1..all_structs_size + 1)
518//! #             .ok_or(ProgramError::InvalidArgument)?;
519//! #
520//! #         fn decode_u16(chunk: &[u8], index: usize) -> u16 {
521//! #             u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap())
522//! #         }
523//! #
524//! #         Ok(all_structs_slice
525//! #             .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
526//! #             .map(|chunk| SecpSignatureOffsets {
527//! #                 signature_offset: decode_u16(chunk, 0),
528//! #                 signature_instruction_index: chunk[2],
529//! #                 eth_address_offset: decode_u16(chunk, 3),
530//! #                 eth_address_instruction_index: chunk[5],
531//! #                 message_data_offset: decode_u16(chunk, 6),
532//! #                 message_data_size: decode_u16(chunk, 8),
533//! #                 message_instruction_index: chunk[10],
534//! #             }))
535//! #     }
536//! # }
537//! use solana_program::{
538//!     account_info::{next_account_info, AccountInfo},
539//!     entrypoint::ProgramResult,
540//!     msg,
541//!     program_error::ProgramError,
542//!     secp256k1_program,
543//!     sysvar,
544//! };
545//!
546//! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct.
547//! struct SecpSignature {
548//!     signature: [u8; secp256k1_defs::SIGNATURE_SERIALIZED_SIZE],
549//!     recovery_id: u8,
550//!     eth_address: [u8; secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE],
551//!     message: Vec<u8>,
552//! }
553//!
554//! /// Load all signatures indicated in the secp256k1 instruction.
555//! ///
556//! /// This function is quite inefficient for reloading the same instructions
557//! /// repeatedly and making copies and allocations.
558//! fn load_signatures(
559//!     secp256k1_instr_data: &[u8],
560//!     instructions_sysvar_account: &AccountInfo,
561//! ) -> Result<Vec<SecpSignature>, ProgramError> {
562//!     let mut sigs = vec![];
563//!     for offsets in secp256k1_defs::iter_signature_offsets(secp256k1_instr_data)? {
564//!         let signature_instr = sysvar::instructions::load_instruction_at_checked(
565//!             offsets.signature_instruction_index as usize,
566//!             instructions_sysvar_account,
567//!         )?;
568//!         let eth_address_instr = sysvar::instructions::load_instruction_at_checked(
569//!             offsets.eth_address_instruction_index as usize,
570//!             instructions_sysvar_account,
571//!         )?;
572//!         let message_instr = sysvar::instructions::load_instruction_at_checked(
573//!             offsets.message_instruction_index as usize,
574//!             instructions_sysvar_account,
575//!         )?;
576//!
577//!         // These indexes must all be valid because the runtime already verified them.
578//!         let signature = &signature_instr.data[offsets.signature_offset as usize
579//!             ..offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE];
580//!         let recovery_id = signature_instr.data
581//!             [offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE];
582//!         let eth_address = &eth_address_instr.data[offsets.eth_address_offset as usize
583//!             ..offsets.eth_address_offset as usize + secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE];
584//!         let message = &message_instr.data[offsets.message_data_offset as usize
585//!             ..offsets.message_data_offset as usize + offsets.message_data_size as usize];
586//!
587//!         let signature =
588//!             <[u8; secp256k1_defs::SIGNATURE_SERIALIZED_SIZE]>::try_from(signature).unwrap();
589//!         let eth_address =
590//!             <[u8; secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE]>::try_from(eth_address).unwrap();
591//!         let message = Vec::from(message);
592//!
593//!         sigs.push(SecpSignature {
594//!             signature,
595//!             recovery_id,
596//!             eth_address,
597//!             message,
598//!         })
599//!     }
600//!     Ok(sigs)
601//! }
602//!
603//! fn demo_secp256k1_custom_many(
604//!     accounts: &[AccountInfo],
605//! ) -> ProgramResult {
606//!     let account_info_iter = &mut accounts.iter();
607//!
608//!     let instructions_sysvar_account = next_account_info(account_info_iter)?;
609//!     assert!(sysvar::instructions::check_id(
610//!         instructions_sysvar_account.key
611//!     ));
612//!
613//!     let secp256k1_instr =
614//!         sysvar::instructions::get_instruction_relative(-1, instructions_sysvar_account)?;
615//!
616//!     assert!(secp256k1_program::check_id(&secp256k1_instr.program_id));
617//!
618//!     let signatures = load_signatures(&secp256k1_instr.data, instructions_sysvar_account)?;
619//!     for (idx, signature_bundle) in signatures.iter().enumerate() {
620//!         let signature = hex::encode(&signature_bundle.signature);
621//!         let eth_address = hex::encode(&signature_bundle.eth_address);
622//!         let message = hex::encode(&signature_bundle.message);
623//!         msg!("sig {}: {:?}", idx, signature);
624//!         msg!("recid: {}: {}", idx, signature_bundle.recovery_id);
625//!         msg!("eth address {}: {}", idx, eth_address);
626//!         msg!("message {}: {}", idx, message);
627//!     }
628//!
629//!     Ok(())
630//! }
631//! ```
632//!
633//! The client program:
634//!
635//! ```no_run
636//! # use solana_sdk::example_mocks::solana_rpc_client;
637//! use anyhow::Result;
638//! use solana_rpc_client::rpc_client::RpcClient;
639//! use solana_sdk::{
640//!     instruction::{AccountMeta, Instruction},
641//!     keccak,
642//!     secp256k1_instruction::{
643//!         self, SecpSignatureOffsets, HASHED_PUBKEY_SERIALIZED_SIZE,
644//!         SIGNATURE_OFFSETS_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE,
645//!     },
646//!     signature::{Keypair, Signer},
647//!     sysvar,
648//!     transaction::Transaction,
649//! };
650//!
651//! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct.
652//! struct SecpSignature {
653//!     signature: [u8; SIGNATURE_SERIALIZED_SIZE],
654//!     recovery_id: u8,
655//!     eth_address: [u8; HASHED_PUBKEY_SERIALIZED_SIZE],
656//!     message: Vec<u8>,
657//! }
658//!
659//! /// Create the instruction data for a secp256k1 instruction.
660//! ///
661//! /// `instruction_index` is the index the secp256k1 instruction will appear
662//! /// within the transaction. For simplicity, this function only supports packing
663//! /// the signatures into the secp256k1 instruction data, and not into any other
664//! /// instructions within the transaction.
665//! fn make_secp256k1_instruction_data(
666//!     signatures: &[SecpSignature],
667//!     instruction_index: u8,
668//! ) -> Result<Vec<u8>> {
669//!     assert!(signatures.len() <= u8::MAX.into());
670//!
671//!     // We're going to pack all the signatures into the secp256k1 instruction data.
672//!     // Before our signatures though is the signature offset structures
673//!     // the secp256k1 program parses to find those signatures.
674//!     // This value represents the byte offset where the signatures begin.
675//!     let data_start = 1 + signatures.len() * SIGNATURE_OFFSETS_SERIALIZED_SIZE;
676//!
677//!     let mut signature_offsets = vec![];
678//!     let mut signature_buffer = vec![];
679//!
680//!     for signature_bundle in signatures {
681//!         let data_start = data_start
682//!             .checked_add(signature_buffer.len())
683//!             .expect("overflow");
684//!
685//!         let signature_offset = data_start;
686//!         let eth_address_offset = data_start
687//!             .checked_add(SIGNATURE_SERIALIZED_SIZE + 1)
688//!             .expect("overflow");
689//!         let message_data_offset = eth_address_offset
690//!             .checked_add(HASHED_PUBKEY_SERIALIZED_SIZE)
691//!             .expect("overflow");
692//!         let message_data_size = signature_bundle.message.len();
693//!
694//!         let signature_offset = u16::try_from(signature_offset)?;
695//!         let eth_address_offset = u16::try_from(eth_address_offset)?;
696//!         let message_data_offset = u16::try_from(message_data_offset)?;
697//!         let message_data_size = u16::try_from(message_data_size)?;
698//!
699//!         signature_offsets.push(SecpSignatureOffsets {
700//!             signature_offset,
701//!             signature_instruction_index: instruction_index,
702//!             eth_address_offset,
703//!             eth_address_instruction_index: instruction_index,
704//!             message_data_offset,
705//!             message_data_size,
706//!             message_instruction_index: instruction_index,
707//!         });
708//!
709//!         signature_buffer.extend(signature_bundle.signature);
710//!         signature_buffer.push(signature_bundle.recovery_id);
711//!         signature_buffer.extend(&signature_bundle.eth_address);
712//!         signature_buffer.extend(&signature_bundle.message);
713//!     }
714//!
715//!     let mut instr_data = vec![];
716//!     instr_data.push(signatures.len() as u8);
717//!
718//!     for offsets in signature_offsets {
719//!         let offsets = bincode::serialize(&offsets)?;
720//!         instr_data.extend(offsets);
721//!     }
722//!
723//!     instr_data.extend(signature_buffer);
724//!
725//!     Ok(instr_data)
726//! }
727//!
728//! fn demo_secp256k1_custom_many(
729//!     payer_keypair: &Keypair,
730//!     client: &RpcClient,
731//!     program_keypair: &Keypair,
732//! ) -> Result<()> {
733//!     // Sign some messages.
734//!     let mut signatures = vec![];
735//!     for idx in 0..2 {
736//!         let secret_key = libsecp256k1::SecretKey::random(&mut rand0_7::thread_rng());
737//!         let message = format!("hello world {}", idx).into_bytes();
738//!         let message_hash = {
739//!             let mut hasher = keccak::Hasher::default();
740//!             hasher.hash(&message);
741//!             hasher.result()
742//!         };
743//!         let secp_message = libsecp256k1::Message::parse(&message_hash.0);
744//!         let (signature, recovery_id) = libsecp256k1::sign(&secp_message, &secret_key);
745//!         let signature = signature.serialize();
746//!         let recovery_id = recovery_id.serialize();
747//!
748//!         let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key);
749//!         let eth_address = secp256k1_instruction::construct_eth_pubkey(&public_key);
750//!
751//!         signatures.push(SecpSignature {
752//!             signature,
753//!             recovery_id,
754//!             eth_address,
755//!             message,
756//!         });
757//!     }
758//!
759//!     let secp256k1_instr_data = make_secp256k1_instruction_data(&signatures, 0)?;
760//!     let secp256k1_instr = Instruction::new_with_bytes(
761//!         solana_sdk::secp256k1_program::ID,
762//!         &secp256k1_instr_data,
763//!         vec![],
764//!     );
765//!
766//!     let program_instr = Instruction::new_with_bytes(
767//!         program_keypair.pubkey(),
768//!         &[],
769//!         vec![
770//!             AccountMeta::new_readonly(sysvar::instructions::ID, false)
771//!         ],
772//!     );
773//!
774//!     let blockhash = client.get_latest_blockhash()?;
775//!     let tx = Transaction::new_signed_with_payer(
776//!         &[secp256k1_instr, program_instr],
777//!         Some(&payer_keypair.pubkey()),
778//!         &[payer_keypair],
779//!         blockhash,
780//!     );
781//!
782//!     client.send_and_confirm_transaction(&tx)?;
783//!
784//!     Ok(())
785//! }
786//! ```
787
788#![cfg(feature = "full")]
789
790use {
791    digest::Digest,
792    serde_derive::{Deserialize, Serialize},
793    solana_feature_set::FeatureSet,
794    solana_instruction::Instruction,
795    solana_precompile_error::PrecompileError,
796};
797
798pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
799pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
800pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
801pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + 1;
802
803/// Offsets of signature data within a secp256k1 instruction.
804///
805/// See the [module documentation][md] for a complete description.
806///
807/// [md]: self
808#[derive(Default, Serialize, Deserialize, Debug, Eq, PartialEq)]
809pub struct SecpSignatureOffsets {
810    /// Offset to 64-byte signature plus 1-byte recovery ID.
811    pub signature_offset: u16,
812    /// Within the transaction, the index of the instruction whose instruction data contains the signature.
813    pub signature_instruction_index: u8,
814    /// Offset to 20-byte Ethereum address.
815    pub eth_address_offset: u16,
816    /// Within the transaction, the index of the instruction whose instruction data contains the address.
817    pub eth_address_instruction_index: u8,
818    /// Offset to start of message data.
819    pub message_data_offset: u16,
820    /// Size of message data in bytes.
821    pub message_data_size: u16,
822    /// Within the transaction, the index of the instruction whose instruction data contains the message.
823    pub message_instruction_index: u8,
824}
825
826/// Sign a message and create a secp256k1 program instruction to verify the signature.
827///
828/// This function is suitable for simple uses of the secp256k1 program.
829/// More complex uses must encode the secp256k1 instruction data manually.
830/// See the [module documentation][md] for examples.
831///
832/// [md]: self
833///
834/// The instruction generated by this function must be the first instruction
835/// included in a transaction or it will not verify. The
836/// [`SecpSignatureOffsets`] structure encoded in the instruction data specify
837/// the instruction indexes as 0.
838///
839/// `message_arr` is hashed with the [`keccak`] hash function prior to signing.
840///
841/// [`keccak`]: crate::keccak
842pub fn new_secp256k1_instruction(
843    priv_key: &libsecp256k1::SecretKey,
844    message_arr: &[u8],
845) -> Instruction {
846    let secp_pubkey = libsecp256k1::PublicKey::from_secret_key(priv_key);
847    let eth_pubkey = construct_eth_pubkey(&secp_pubkey);
848    let mut hasher = sha3::Keccak256::new();
849    hasher.update(message_arr);
850    let message_hash = hasher.finalize();
851    let mut message_hash_arr = [0u8; 32];
852    message_hash_arr.copy_from_slice(message_hash.as_slice());
853    let message = libsecp256k1::Message::parse(&message_hash_arr);
854    let (signature, recovery_id) = libsecp256k1::sign(&message, priv_key);
855    let signature_arr = signature.serialize();
856    assert_eq!(signature_arr.len(), SIGNATURE_SERIALIZED_SIZE);
857
858    let instruction_data_len = DATA_START
859        .saturating_add(eth_pubkey.len())
860        .saturating_add(signature_arr.len())
861        .saturating_add(message_arr.len())
862        .saturating_add(1);
863    let mut instruction_data = vec![0; instruction_data_len];
864
865    let eth_address_offset = DATA_START;
866    instruction_data[eth_address_offset..eth_address_offset.saturating_add(eth_pubkey.len())]
867        .copy_from_slice(&eth_pubkey);
868
869    let signature_offset = DATA_START.saturating_add(eth_pubkey.len());
870    instruction_data[signature_offset..signature_offset.saturating_add(signature_arr.len())]
871        .copy_from_slice(&signature_arr);
872
873    instruction_data[signature_offset.saturating_add(signature_arr.len())] =
874        recovery_id.serialize();
875
876    let message_data_offset = signature_offset
877        .saturating_add(signature_arr.len())
878        .saturating_add(1);
879    instruction_data[message_data_offset..].copy_from_slice(message_arr);
880
881    let num_signatures = 1;
882    instruction_data[0] = num_signatures;
883    let offsets = SecpSignatureOffsets {
884        signature_offset: signature_offset as u16,
885        signature_instruction_index: 0,
886        eth_address_offset: eth_address_offset as u16,
887        eth_address_instruction_index: 0,
888        message_data_offset: message_data_offset as u16,
889        message_data_size: message_arr.len() as u16,
890        message_instruction_index: 0,
891    };
892    let writer = std::io::Cursor::new(&mut instruction_data[1..DATA_START]);
893    bincode::serialize_into(writer, &offsets).unwrap();
894
895    Instruction {
896        program_id: solana_sdk::secp256k1_program::id(),
897        accounts: vec![],
898        data: instruction_data,
899    }
900}
901
902/// Creates an Ethereum address from a secp256k1 public key.
903pub fn construct_eth_pubkey(
904    pubkey: &libsecp256k1::PublicKey,
905) -> [u8; HASHED_PUBKEY_SERIALIZED_SIZE] {
906    let mut addr = [0u8; HASHED_PUBKEY_SERIALIZED_SIZE];
907    addr.copy_from_slice(&sha3::Keccak256::digest(&pubkey.serialize()[1..])[12..]);
908    assert_eq!(addr.len(), HASHED_PUBKEY_SERIALIZED_SIZE);
909    addr
910}
911
912/// Verifies the signatures specified in the secp256k1 instruction data.
913///
914/// This is same the verification routine executed by the runtime's secp256k1 native program,
915/// and is primarily of use to the runtime.
916///
917/// `data` is the secp256k1 program's instruction data. `instruction_datas` is
918/// the full slice of instruction datas for all instructions in the transaction,
919/// including the secp256k1 program's instruction data.
920///
921/// `feature_set` is the set of active Solana features. It is used to enable or
922/// disable a few minor additional checks that were activated on chain
923/// subsequent to the addition of the secp256k1 native program. For many
924/// purposes passing `FeatureSet::all_enabled()` is reasonable.
925pub fn verify(
926    data: &[u8],
927    instruction_datas: &[&[u8]],
928    _feature_set: &FeatureSet,
929) -> Result<(), PrecompileError> {
930    if data.is_empty() {
931        return Err(PrecompileError::InvalidInstructionDataSize);
932    }
933    let count = data[0] as usize;
934    if count == 0 && data.len() > 1 {
935        // count is zero but the instruction data indicates that is probably not
936        // correct, fail the instruction to catch probable invalid secp256k1
937        // instruction construction.
938        return Err(PrecompileError::InvalidInstructionDataSize);
939    }
940    let expected_data_size = count
941        .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
942        .saturating_add(1);
943    if data.len() < expected_data_size {
944        return Err(PrecompileError::InvalidInstructionDataSize);
945    }
946    for i in 0..count {
947        let start = i
948            .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
949            .saturating_add(1);
950        let end = start.saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE);
951
952        let offsets: SecpSignatureOffsets = bincode::deserialize(&data[start..end])
953            .map_err(|_| PrecompileError::InvalidSignature)?;
954
955        // Parse out signature
956        let signature_index = offsets.signature_instruction_index as usize;
957        if signature_index >= instruction_datas.len() {
958            return Err(PrecompileError::InvalidInstructionDataSize);
959        }
960        let signature_instruction = instruction_datas[signature_index];
961        let sig_start = offsets.signature_offset as usize;
962        let sig_end = sig_start.saturating_add(SIGNATURE_SERIALIZED_SIZE);
963        if sig_end >= signature_instruction.len() {
964            return Err(PrecompileError::InvalidSignature);
965        }
966
967        let signature = libsecp256k1::Signature::parse_standard_slice(
968            &signature_instruction[sig_start..sig_end],
969        )
970        .map_err(|_| PrecompileError::InvalidSignature)?;
971
972        let recovery_id = libsecp256k1::RecoveryId::parse(signature_instruction[sig_end])
973            .map_err(|_| PrecompileError::InvalidRecoveryId)?;
974
975        // Parse out pubkey
976        let eth_address_slice = get_data_slice(
977            instruction_datas,
978            offsets.eth_address_instruction_index,
979            offsets.eth_address_offset,
980            HASHED_PUBKEY_SERIALIZED_SIZE,
981        )?;
982
983        // Parse out message
984        let message_slice = get_data_slice(
985            instruction_datas,
986            offsets.message_instruction_index,
987            offsets.message_data_offset,
988            offsets.message_data_size as usize,
989        )?;
990
991        let mut hasher = sha3::Keccak256::new();
992        hasher.update(message_slice);
993        let message_hash = hasher.finalize();
994
995        let pubkey = libsecp256k1::recover(
996            &libsecp256k1::Message::parse_slice(&message_hash).unwrap(),
997            &signature,
998            &recovery_id,
999        )
1000        .map_err(|_| PrecompileError::InvalidSignature)?;
1001        let eth_address = construct_eth_pubkey(&pubkey);
1002
1003        if eth_address_slice != eth_address {
1004            return Err(PrecompileError::InvalidSignature);
1005        }
1006    }
1007    Ok(())
1008}
1009
1010fn get_data_slice<'a>(
1011    instruction_datas: &'a [&[u8]],
1012    instruction_index: u8,
1013    offset_start: u16,
1014    size: usize,
1015) -> Result<&'a [u8], PrecompileError> {
1016    let signature_index = instruction_index as usize;
1017    if signature_index >= instruction_datas.len() {
1018        return Err(PrecompileError::InvalidDataOffsets);
1019    }
1020    let signature_instruction = &instruction_datas[signature_index];
1021    let start = offset_start as usize;
1022    let end = start.saturating_add(size);
1023    if end > signature_instruction.len() {
1024        return Err(PrecompileError::InvalidSignature);
1025    }
1026
1027    Ok(&instruction_datas[signature_index][start..end])
1028}
1029
1030#[cfg(test)]
1031pub mod test {
1032    use {
1033        super::*,
1034        crate::{
1035            hash::Hash,
1036            keccak,
1037            secp256k1_instruction::{
1038                new_secp256k1_instruction, SecpSignatureOffsets, SIGNATURE_OFFSETS_SERIALIZED_SIZE,
1039            },
1040            signature::{Keypair, Signer},
1041            transaction::Transaction,
1042        },
1043        rand0_7::{thread_rng, Rng},
1044    };
1045
1046    fn test_case(
1047        num_signatures: u8,
1048        offsets: &SecpSignatureOffsets,
1049    ) -> Result<(), PrecompileError> {
1050        let mut instruction_data = vec![0u8; DATA_START];
1051        instruction_data[0] = num_signatures;
1052        let writer = std::io::Cursor::new(&mut instruction_data[1..]);
1053        bincode::serialize_into(writer, &offsets).unwrap();
1054        let feature_set = FeatureSet::all_enabled();
1055        verify(&instruction_data, &[&[0u8; 100]], &feature_set)
1056    }
1057
1058    #[test]
1059    fn test_invalid_offsets() {
1060        solana_logger::setup();
1061
1062        let mut instruction_data = vec![0u8; DATA_START];
1063        let offsets = SecpSignatureOffsets::default();
1064        instruction_data[0] = 1;
1065        let writer = std::io::Cursor::new(&mut instruction_data[1..]);
1066        bincode::serialize_into(writer, &offsets).unwrap();
1067        instruction_data.truncate(instruction_data.len() - 1);
1068        let feature_set = FeatureSet::all_enabled();
1069
1070        assert_eq!(
1071            verify(&instruction_data, &[&[0u8; 100]], &feature_set),
1072            Err(PrecompileError::InvalidInstructionDataSize)
1073        );
1074
1075        let offsets = SecpSignatureOffsets {
1076            signature_instruction_index: 1,
1077            ..SecpSignatureOffsets::default()
1078        };
1079        assert_eq!(
1080            test_case(1, &offsets),
1081            Err(PrecompileError::InvalidInstructionDataSize)
1082        );
1083
1084        let offsets = SecpSignatureOffsets {
1085            message_instruction_index: 1,
1086            ..SecpSignatureOffsets::default()
1087        };
1088        assert_eq!(
1089            test_case(1, &offsets),
1090            Err(PrecompileError::InvalidDataOffsets)
1091        );
1092
1093        let offsets = SecpSignatureOffsets {
1094            eth_address_instruction_index: 1,
1095            ..SecpSignatureOffsets::default()
1096        };
1097        assert_eq!(
1098            test_case(1, &offsets),
1099            Err(PrecompileError::InvalidDataOffsets)
1100        );
1101    }
1102
1103    #[test]
1104    fn test_message_data_offsets() {
1105        let offsets = SecpSignatureOffsets {
1106            message_data_offset: 99,
1107            message_data_size: 1,
1108            ..SecpSignatureOffsets::default()
1109        };
1110        assert_eq!(
1111            test_case(1, &offsets),
1112            Err(PrecompileError::InvalidSignature)
1113        );
1114
1115        let offsets = SecpSignatureOffsets {
1116            message_data_offset: 100,
1117            message_data_size: 1,
1118            ..SecpSignatureOffsets::default()
1119        };
1120        assert_eq!(
1121            test_case(1, &offsets),
1122            Err(PrecompileError::InvalidSignature)
1123        );
1124
1125        let offsets = SecpSignatureOffsets {
1126            message_data_offset: 100,
1127            message_data_size: 1000,
1128            ..SecpSignatureOffsets::default()
1129        };
1130        assert_eq!(
1131            test_case(1, &offsets),
1132            Err(PrecompileError::InvalidSignature)
1133        );
1134
1135        let offsets = SecpSignatureOffsets {
1136            message_data_offset: u16::MAX,
1137            message_data_size: u16::MAX,
1138            ..SecpSignatureOffsets::default()
1139        };
1140        assert_eq!(
1141            test_case(1, &offsets),
1142            Err(PrecompileError::InvalidSignature)
1143        );
1144    }
1145
1146    #[test]
1147    fn test_eth_offset() {
1148        let offsets = SecpSignatureOffsets {
1149            eth_address_offset: u16::MAX,
1150            ..SecpSignatureOffsets::default()
1151        };
1152        assert_eq!(
1153            test_case(1, &offsets),
1154            Err(PrecompileError::InvalidSignature)
1155        );
1156
1157        let offsets = SecpSignatureOffsets {
1158            eth_address_offset: 100 - HASHED_PUBKEY_SERIALIZED_SIZE as u16 + 1,
1159            ..SecpSignatureOffsets::default()
1160        };
1161        assert_eq!(
1162            test_case(1, &offsets),
1163            Err(PrecompileError::InvalidSignature)
1164        );
1165    }
1166
1167    #[test]
1168    fn test_signature_offset() {
1169        let offsets = SecpSignatureOffsets {
1170            signature_offset: u16::MAX,
1171            ..SecpSignatureOffsets::default()
1172        };
1173        assert_eq!(
1174            test_case(1, &offsets),
1175            Err(PrecompileError::InvalidSignature)
1176        );
1177
1178        let offsets = SecpSignatureOffsets {
1179            signature_offset: 100 - SIGNATURE_SERIALIZED_SIZE as u16 + 1,
1180            ..SecpSignatureOffsets::default()
1181        };
1182        assert_eq!(
1183            test_case(1, &offsets),
1184            Err(PrecompileError::InvalidSignature)
1185        );
1186    }
1187
1188    #[test]
1189    fn test_count_is_zero_but_sig_data_exists() {
1190        solana_logger::setup();
1191
1192        let mut instruction_data = vec![0u8; DATA_START];
1193        let offsets = SecpSignatureOffsets::default();
1194        instruction_data[0] = 0;
1195        let writer = std::io::Cursor::new(&mut instruction_data[1..]);
1196        bincode::serialize_into(writer, &offsets).unwrap();
1197        let feature_set = FeatureSet::all_enabled();
1198
1199        assert_eq!(
1200            verify(&instruction_data, &[&[0u8; 100]], &feature_set),
1201            Err(PrecompileError::InvalidInstructionDataSize)
1202        );
1203    }
1204
1205    #[test]
1206    fn test_secp256k1() {
1207        solana_logger::setup();
1208        let offsets = SecpSignatureOffsets::default();
1209        assert_eq!(
1210            bincode::serialized_size(&offsets).unwrap() as usize,
1211            SIGNATURE_OFFSETS_SERIALIZED_SIZE
1212        );
1213
1214        let secp_privkey = libsecp256k1::SecretKey::random(&mut thread_rng());
1215        let message_arr = b"hello";
1216        let mut secp_instruction = new_secp256k1_instruction(&secp_privkey, message_arr);
1217        let mint_keypair = Keypair::new();
1218        let feature_set = solana_feature_set::FeatureSet::all_enabled();
1219
1220        let tx = Transaction::new_signed_with_payer(
1221            &[secp_instruction.clone()],
1222            Some(&mint_keypair.pubkey()),
1223            &[&mint_keypair],
1224            Hash::default(),
1225        );
1226
1227        assert!(tx.verify_precompiles(&feature_set).is_ok());
1228
1229        let index = thread_rng().gen_range(0, secp_instruction.data.len());
1230        secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12);
1231        let tx = Transaction::new_signed_with_payer(
1232            &[secp_instruction],
1233            Some(&mint_keypair.pubkey()),
1234            &[&mint_keypair],
1235            Hash::default(),
1236        );
1237        assert!(tx.verify_precompiles(&feature_set).is_err());
1238    }
1239
1240    // Signatures are malleable.
1241    #[test]
1242    fn test_malleability() {
1243        solana_logger::setup();
1244
1245        let secret_key = libsecp256k1::SecretKey::random(&mut thread_rng());
1246        let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key);
1247        let eth_address = construct_eth_pubkey(&public_key);
1248
1249        let message = b"hello";
1250        let message_hash = {
1251            let mut hasher = keccak::Hasher::default();
1252            hasher.hash(message);
1253            hasher.result()
1254        };
1255
1256        let secp_message = libsecp256k1::Message::parse(&message_hash.0);
1257        let (signature, recovery_id) = libsecp256k1::sign(&secp_message, &secret_key);
1258
1259        // Flip the S value in the signature to make a different but valid signature.
1260        let mut alt_signature = signature;
1261        alt_signature.s = -alt_signature.s;
1262        let alt_recovery_id = libsecp256k1::RecoveryId::parse(recovery_id.serialize() ^ 1).unwrap();
1263
1264        let mut data: Vec<u8> = vec![];
1265        let mut both_offsets = vec![];
1266
1267        // Verify both signatures of the same message.
1268        let sigs = [(signature, recovery_id), (alt_signature, alt_recovery_id)];
1269        for (signature, recovery_id) in sigs.iter() {
1270            let signature_offset = data.len();
1271            data.extend(signature.serialize());
1272            data.push(recovery_id.serialize());
1273            let eth_address_offset = data.len();
1274            data.extend(eth_address);
1275            let message_data_offset = data.len();
1276            data.extend(message);
1277
1278            let data_start = 1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE * 2;
1279
1280            let offsets = SecpSignatureOffsets {
1281                signature_offset: (signature_offset + data_start) as u16,
1282                signature_instruction_index: 0,
1283                eth_address_offset: (eth_address_offset + data_start) as u16,
1284                eth_address_instruction_index: 0,
1285                message_data_offset: (message_data_offset + data_start) as u16,
1286                message_data_size: message.len() as u16,
1287                message_instruction_index: 0,
1288            };
1289
1290            both_offsets.push(offsets);
1291        }
1292
1293        let mut instruction_data: Vec<u8> = vec![2];
1294
1295        for offsets in both_offsets {
1296            let offsets = bincode::serialize(&offsets).unwrap();
1297            instruction_data.extend(offsets);
1298        }
1299
1300        instruction_data.extend(data);
1301
1302        verify(
1303            &instruction_data,
1304            &[&instruction_data],
1305            &FeatureSet::all_enabled(),
1306        )
1307        .unwrap();
1308    }
1309}