lightcone_sdk/program/
ed25519.rs1use solana_sdk::{
11 instruction::Instruction,
12 pubkey::Pubkey,
13};
14
15use crate::program::constants::ED25519_PROGRAM_ID;
16use crate::program::orders::FullOrder;
17
18#[derive(Debug, Clone)]
24pub struct Ed25519VerifyParams {
25 pub pubkey: Pubkey,
27 pub message: [u8; 32],
29 pub signature: [u8; 64],
31}
32
33impl Ed25519VerifyParams {
34 pub fn from_order(order: &FullOrder) -> Self {
36 Self {
37 pubkey: order.maker,
38 message: order.hash(),
39 signature: order.signature,
40 }
41 }
42}
43
44pub fn create_ed25519_verify_instruction(params: &Ed25519VerifyParams) -> Instruction {
69 let mut data = vec![0u8; 144];
70
71 data[0] = 1; data[1] = 0; data[2..4].copy_from_slice(&16u16.to_le_bytes());
77 data[4..6].copy_from_slice(&0xFFFFu16.to_le_bytes());
79
80 data[6..8].copy_from_slice(&80u16.to_le_bytes());
82 data[8..10].copy_from_slice(&0xFFFFu16.to_le_bytes());
84
85 data[10..12].copy_from_slice(&112u16.to_le_bytes());
87 data[12..14].copy_from_slice(&32u16.to_le_bytes());
89 data[14..16].copy_from_slice(&0xFFFFu16.to_le_bytes());
91
92 data[16..80].copy_from_slice(¶ms.signature);
94
95 data[80..112].copy_from_slice(params.pubkey.as_ref());
97
98 data[112..144].copy_from_slice(¶ms.message);
100
101 Instruction {
102 program_id: ED25519_PROGRAM_ID,
103 accounts: vec![],
104 data,
105 }
106}
107
108pub fn create_ed25519_verify_instructions(params: &[Ed25519VerifyParams]) -> Vec<Instruction> {
110 params.iter().map(create_ed25519_verify_instruction).collect()
111}
112
113pub fn create_order_verify_instruction(order: &FullOrder) -> Instruction {
115 let params = Ed25519VerifyParams::from_order(order);
116 create_ed25519_verify_instruction(¶ms)
117}
118
119pub fn create_batch_ed25519_verify_instruction(params: &[Ed25519VerifyParams]) -> Instruction {
128 assert!(!params.is_empty(), "At least one signature is required");
129
130 let num_signatures = params.len();
131
132 let header_size = 2 + num_signatures * 14;
134
135 let entry_size = 64 + 32 + 32;
138 let total_size = header_size + num_signatures * entry_size;
139
140 let mut data = vec![0u8; total_size];
141
142 data[0] = num_signatures as u8;
144 data[1] = 0;
146
147 let mut header_offset = 2;
149 for i in 0..num_signatures {
150 let data_start = header_size + i * entry_size;
151
152 data[header_offset..header_offset + 2].copy_from_slice(&(data_start as u16).to_le_bytes());
154 header_offset += 2;
155
156 data[header_offset..header_offset + 2].copy_from_slice(&0xFFFFu16.to_le_bytes());
158 header_offset += 2;
159
160 data[header_offset..header_offset + 2]
162 .copy_from_slice(&((data_start + 64) as u16).to_le_bytes());
163 header_offset += 2;
164
165 data[header_offset..header_offset + 2].copy_from_slice(&0xFFFFu16.to_le_bytes());
167 header_offset += 2;
168
169 data[header_offset..header_offset + 2]
171 .copy_from_slice(&((data_start + 64 + 32) as u16).to_le_bytes());
172 header_offset += 2;
173
174 data[header_offset..header_offset + 2].copy_from_slice(&32u16.to_le_bytes());
176 header_offset += 2;
177
178 data[header_offset..header_offset + 2].copy_from_slice(&0xFFFFu16.to_le_bytes());
180 header_offset += 2;
181 }
182
183 for (i, p) in params.iter().enumerate() {
185 let data_start = header_size + i * entry_size;
186 data[data_start..data_start + 64].copy_from_slice(&p.signature);
187 data[data_start + 64..data_start + 96].copy_from_slice(p.pubkey.as_ref());
188 data[data_start + 96..data_start + 128].copy_from_slice(&p.message);
189 }
190
191 Instruction {
192 program_id: ED25519_PROGRAM_ID,
193 accounts: vec![],
194 data,
195 }
196}
197
198#[derive(Debug)]
215pub struct MatchIxOffsets;
216
217impl MatchIxOffsets {
218 pub const TAKER_MESSAGE: u16 = 1;
220 pub const TAKER_PUBKEY: u16 = 41;
222 pub const TAKER_SIGNATURE: u16 = 98;
224 pub const NUM_MAKERS: u16 = 162;
226
227 pub fn maker_offsets(maker_index: usize) -> MakerOffsets {
230 let base = 163 + maker_index * 169;
231 MakerOffsets {
232 message: base as u16,
233 pubkey: (base + 32 + 8) as u16,
234 signature: (base + 32 + 65) as u16,
235 }
236 }
237}
238
239#[derive(Debug)]
241pub struct MakerOffsets {
242 pub message: u16,
244 pub pubkey: u16,
246 pub signature: u16,
248}
249
250#[derive(Debug, Clone)]
252pub struct CrossRefEd25519Params {
253 pub signature_offset: u16,
254 pub signature_ix_index: u16,
255 pub pubkey_offset: u16,
256 pub pubkey_ix_index: u16,
257 pub message_offset: u16,
258 pub message_size: u16,
259 pub message_ix_index: u16,
260}
261
262pub fn create_cross_ref_ed25519_instruction(params: &CrossRefEd25519Params) -> Instruction {
266 let mut data = vec![0u8; 16];
267
268 data[0] = 1;
270 data[1] = 0;
272
273 data[2..4].copy_from_slice(¶ms.signature_offset.to_le_bytes());
275 data[4..6].copy_from_slice(¶ms.signature_ix_index.to_le_bytes());
277
278 data[6..8].copy_from_slice(¶ms.pubkey_offset.to_le_bytes());
280 data[8..10].copy_from_slice(¶ms.pubkey_ix_index.to_le_bytes());
282
283 data[10..12].copy_from_slice(¶ms.message_offset.to_le_bytes());
285 data[12..14].copy_from_slice(¶ms.message_size.to_le_bytes());
287 data[14..16].copy_from_slice(¶ms.message_ix_index.to_le_bytes());
289
290 Instruction {
291 program_id: ED25519_PROGRAM_ID,
292 accounts: vec![],
293 data,
294 }
295}
296
297pub fn create_cross_ref_ed25519_instructions(
311 num_makers: usize,
312 match_ix_index: u16,
313) -> Vec<Instruction> {
314 let mut instructions = Vec::with_capacity(1 + num_makers);
315
316 let taker_ix = create_cross_ref_ed25519_instruction(&CrossRefEd25519Params {
318 signature_offset: MatchIxOffsets::TAKER_SIGNATURE,
319 signature_ix_index: match_ix_index,
320 pubkey_offset: MatchIxOffsets::TAKER_PUBKEY,
321 pubkey_ix_index: match_ix_index,
322 message_offset: MatchIxOffsets::TAKER_MESSAGE,
323 message_size: 32,
324 message_ix_index: match_ix_index,
325 });
326 instructions.push(taker_ix);
327
328 for i in 0..num_makers {
330 let offsets = MatchIxOffsets::maker_offsets(i);
331 let maker_ix = create_cross_ref_ed25519_instruction(&CrossRefEd25519Params {
332 signature_offset: offsets.signature,
333 signature_ix_index: match_ix_index,
334 pubkey_offset: offsets.pubkey,
335 pubkey_ix_index: match_ix_index,
336 message_offset: offsets.message,
337 message_size: 32,
338 message_ix_index: match_ix_index,
339 });
340 instructions.push(maker_ix);
341 }
342
343 instructions
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_ed25519_verify_instruction_size() {
352 let params = Ed25519VerifyParams {
353 pubkey: Pubkey::new_unique(),
354 message: [42u8; 32],
355 signature: [0u8; 64],
356 };
357
358 let ix = create_ed25519_verify_instruction(¶ms);
359 assert_eq!(ix.data.len(), 144);
360 assert_eq!(ix.program_id, ED25519_PROGRAM_ID);
361 assert!(ix.accounts.is_empty());
362 }
363
364 #[test]
365 fn test_batch_ed25519_verify_instruction_size() {
366 let params = vec![
367 Ed25519VerifyParams {
368 pubkey: Pubkey::new_unique(),
369 message: [1u8; 32],
370 signature: [0u8; 64],
371 },
372 Ed25519VerifyParams {
373 pubkey: Pubkey::new_unique(),
374 message: [2u8; 32],
375 signature: [0u8; 64],
376 },
377 ];
378
379 let ix = create_batch_ed25519_verify_instruction(¶ms);
380
381 assert_eq!(ix.data.len(), 30 + 256);
385 }
386
387 #[test]
388 fn test_cross_ref_ed25519_instruction_size() {
389 let params = CrossRefEd25519Params {
390 signature_offset: 98,
391 signature_ix_index: 2,
392 pubkey_offset: 41,
393 pubkey_ix_index: 2,
394 message_offset: 1,
395 message_size: 32,
396 message_ix_index: 2,
397 };
398
399 let ix = create_cross_ref_ed25519_instruction(¶ms);
400 assert_eq!(ix.data.len(), 16);
401 }
402
403 #[test]
404 fn test_cross_ref_instructions_count() {
405 let instructions = create_cross_ref_ed25519_instructions(2, 3);
406 assert_eq!(instructions.len(), 3);
408 }
409
410 #[test]
411 fn test_maker_offsets() {
412 let offsets0 = MatchIxOffsets::maker_offsets(0);
414 assert_eq!(offsets0.message, 163);
415 assert_eq!(offsets0.pubkey, 163 + 32 + 8);
416 assert_eq!(offsets0.signature, 163 + 32 + 65);
417
418 let offsets1 = MatchIxOffsets::maker_offsets(1);
420 assert_eq!(offsets1.message, 163 + 169);
421 }
422}