solana_ed25519_program/
lib.rs

1//! Instructions for the [ed25519 native program][np].
2//!
3//! [np]: https://docs.solanalabs.com/runtime/programs#ed25519-program
4
5use {
6    bytemuck::bytes_of,
7    bytemuck_derive::{Pod, Zeroable},
8    solana_instruction::Instruction,
9};
10
11pub const PUBKEY_SERIALIZED_SIZE: usize = 32;
12pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
13pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
14// bytemuck requires structures to be aligned
15pub const SIGNATURE_OFFSETS_START: usize = 2;
16pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
17
18#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)]
19#[repr(C)]
20pub struct Ed25519SignatureOffsets {
21    pub signature_offset: u16, // offset to ed25519 signature of 64 bytes
22    pub signature_instruction_index: u16, // instruction index to find signature
23    pub public_key_offset: u16, // offset to public key of 32 bytes
24    pub public_key_instruction_index: u16, // instruction index to find public key
25    pub message_data_offset: u16, // offset to start of message data
26    pub message_data_size: u16, // size of message data
27    pub message_instruction_index: u16, // index of instruction data to get message data
28}
29
30/// Encode just the signature offsets in a single ed25519 instruction.
31///
32/// This is a convenience function for rare cases where we wish to verify multiple messages in
33/// the same instruction. The verification data can be stored in a separate instruction specified
34/// by the `*_instruction_index` fields of `offsets`, or in this instruction by extending the data
35/// buffer.
36///
37/// Note: If the signer for these messages are the same, it is cheaper to concatenate the messages
38/// and have the signer sign the single buffer and use [`new_ed25519_instruction_with_signature`].
39pub fn offsets_to_ed25519_instruction(offsets: &[Ed25519SignatureOffsets]) -> Instruction {
40    let mut instruction_data = Vec::with_capacity(
41        SIGNATURE_OFFSETS_START
42            .saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE.saturating_mul(offsets.len())),
43    );
44
45    let num_signatures = offsets.len() as u16;
46    instruction_data.extend_from_slice(&num_signatures.to_le_bytes());
47
48    for offsets in offsets {
49        instruction_data.extend_from_slice(bytes_of(offsets));
50    }
51
52    Instruction {
53        program_id: solana_sdk_ids::ed25519_program::id(),
54        accounts: vec![],
55        data: instruction_data,
56    }
57}
58
59pub fn new_ed25519_instruction_with_signature(
60    message: &[u8],
61    signature: &[u8; SIGNATURE_SERIALIZED_SIZE],
62    pubkey: &[u8; PUBKEY_SERIALIZED_SIZE],
63) -> Instruction {
64    let mut instruction_data = Vec::with_capacity(
65        DATA_START
66            .saturating_add(SIGNATURE_SERIALIZED_SIZE)
67            .saturating_add(PUBKEY_SERIALIZED_SIZE)
68            .saturating_add(message.len()),
69    );
70
71    let num_signatures: u8 = 1;
72    let public_key_offset = DATA_START;
73    let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE);
74    let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
75
76    // add padding byte so that offset structure is aligned
77    instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
78
79    let offsets = Ed25519SignatureOffsets {
80        signature_offset: signature_offset as u16,
81        signature_instruction_index: u16::MAX,
82        public_key_offset: public_key_offset as u16,
83        public_key_instruction_index: u16::MAX,
84        message_data_offset: message_data_offset as u16,
85        message_data_size: message.len() as u16,
86        message_instruction_index: u16::MAX,
87    };
88
89    instruction_data.extend_from_slice(bytes_of(&offsets));
90
91    debug_assert_eq!(instruction_data.len(), public_key_offset);
92
93    instruction_data.extend_from_slice(pubkey);
94
95    debug_assert_eq!(instruction_data.len(), signature_offset);
96
97    instruction_data.extend_from_slice(signature);
98
99    debug_assert_eq!(instruction_data.len(), message_data_offset);
100
101    instruction_data.extend_from_slice(message);
102
103    Instruction {
104        program_id: solana_sdk_ids::ed25519_program::id(),
105        accounts: vec![],
106        data: instruction_data,
107    }
108}