1use crate::{Address, ProgramError};
4
5pub type Sha256Hash = [u8; 32];
6pub type Keccak256Hash = [u8; 32];
7pub type Blake3Hash = [u8; 32];
8pub type Secp256k1Pubkey = [u8; 64];
9pub type EthereumAddress = [u8; 20];
10pub type CurvePoint = [u8; 32];
11pub type CurveScalar = [u8; 32];
12pub type PoseidonHash = [u8; 32];
13pub type AltBn128G1 = [u8; 64];
14pub type AltBn128G1Compressed = [u8; 32];
15pub type AltBn128G2 = [u8; 128];
16pub type AltBn128G2Compressed = [u8; 64];
17pub type AltBn128PairingResult = [u8; 32];
18
19pub const MAX_HASH_SEGMENTS: usize = 16;
20pub const CURVE25519_EDWARDS: u64 = 0;
21pub const CURVE25519_RISTRETTO: u64 = 1;
22pub const CURVE_GROUP_ADD: u64 = 0;
23pub const CURVE_GROUP_SUB: u64 = 1;
24pub const CURVE_GROUP_MUL: u64 = 2;
25pub const POSEIDON_BN254_X5: u64 = 0;
26pub const POSEIDON_BIG_ENDIAN: u64 = 0;
27pub const POSEIDON_LITTLE_ENDIAN: u64 = 1;
28pub const MAX_POSEIDON_INPUTS: usize = 12;
29pub const POSEIDON_INPUT_LEN: usize = 32;
30pub const ALT_BN128_LE_FLAG: u64 = 0x80;
31pub const ALT_BN128_FIELD_SIZE: usize = 32;
32pub const ALT_BN128_G1_POINT_SIZE: usize = 64;
33pub const ALT_BN128_G2_POINT_SIZE: usize = 128;
34pub const ALT_BN128_G1_ADDITION_INPUT_SIZE: usize = 128;
35pub const ALT_BN128_G2_ADDITION_INPUT_SIZE: usize = 256;
36pub const ALT_BN128_G1_MULTIPLICATION_INPUT_SIZE: usize = 96;
37pub const ALT_BN128_G2_MULTIPLICATION_INPUT_SIZE: usize = 160;
38pub const ALT_BN128_PAIRING_ELEMENT_SIZE: usize = 192;
39pub const ALT_BN128_G1_ADD_BE: u64 = 0;
40pub const ALT_BN128_G1_SUB_BE: u64 = 1;
41pub const ALT_BN128_G1_MUL_BE: u64 = 2;
42pub const ALT_BN128_PAIRING_BE: u64 = 3;
43pub const ALT_BN128_G2_ADD_BE: u64 = 4;
44pub const ALT_BN128_G2_SUB_BE: u64 = 5;
45pub const ALT_BN128_G2_MUL_BE: u64 = 6;
46pub const ALT_BN128_G1_ADD_LE: u64 = ALT_BN128_G1_ADD_BE | ALT_BN128_LE_FLAG;
47pub const ALT_BN128_G1_SUB_LE: u64 = ALT_BN128_G1_SUB_BE | ALT_BN128_LE_FLAG;
48pub const ALT_BN128_G1_MUL_LE: u64 = ALT_BN128_G1_MUL_BE | ALT_BN128_LE_FLAG;
49pub const ALT_BN128_PAIRING_LE: u64 = ALT_BN128_PAIRING_BE | ALT_BN128_LE_FLAG;
50pub const ALT_BN128_G2_ADD_LE: u64 = ALT_BN128_G2_ADD_BE | ALT_BN128_LE_FLAG;
51pub const ALT_BN128_G2_SUB_LE: u64 = ALT_BN128_G2_SUB_BE | ALT_BN128_LE_FLAG;
52pub const ALT_BN128_G2_MUL_LE: u64 = ALT_BN128_G2_MUL_BE | ALT_BN128_LE_FLAG;
53pub const ALT_BN128_G1_COMPRESS_BE: u64 = 0;
54pub const ALT_BN128_G1_DECOMPRESS_BE: u64 = 1;
55pub const ALT_BN128_G2_COMPRESS_BE: u64 = 2;
56pub const ALT_BN128_G2_DECOMPRESS_BE: u64 = 3;
57pub const ALT_BN128_G1_COMPRESS_LE: u64 = ALT_BN128_G1_COMPRESS_BE | ALT_BN128_LE_FLAG;
58pub const ALT_BN128_G1_DECOMPRESS_LE: u64 = ALT_BN128_G1_DECOMPRESS_BE | ALT_BN128_LE_FLAG;
59pub const ALT_BN128_G2_COMPRESS_LE: u64 = ALT_BN128_G2_COMPRESS_BE | ALT_BN128_LE_FLAG;
60pub const ALT_BN128_G2_DECOMPRESS_LE: u64 = ALT_BN128_G2_DECOMPRESS_BE | ALT_BN128_LE_FLAG;
61pub const MAX_INSTRUCTION_DATA_LEN: usize = 1232;
62pub const MAX_INSTRUCTION_ACCOUNTS_BYTES: usize = 2176;
63
64pub const ED25519_PROGRAM_ID: Address = Address::new_from_array(crate::__decode_base58_32(
65 "Ed25519SigVerify111111111111111111111111111",
66));
67
68pub const SECP256K1_PROGRAM_ID: Address = Address::new_from_array(crate::__decode_base58_32(
69 "KeccakSecp256k11111111111111111111111111111",
70));
71
72pub const SECP256R1_PROGRAM_ID: Address = Address::new_from_array(crate::__decode_base58_32(
75 "Secp256r1SigVerify1111111111111111111111111",
76));
77
78#[derive(Clone, Debug)]
79pub struct ProcessedInstruction {
80 pub program_id: Address,
81 pub data: [u8; MAX_INSTRUCTION_DATA_LEN],
82 pub data_len: usize,
83 pub accounts_len: usize,
84}
85
86#[derive(Clone, Debug)]
87pub struct ProcessedInstructionData<const MAX_DATA: usize> {
88 pub program_id: Address,
89 pub data: [u8; MAX_DATA],
90 pub data_len: usize,
91}
92
93pub use hopper_native::introspect::ProcessedInstructionAccount;
95
96#[derive(Debug)]
98pub struct ProcessedInstructionView<'a> {
99 pub program_id: Address,
100 pub data: &'a [u8],
101 pub accounts: &'a [ProcessedInstructionAccount],
102}
103
104#[cfg(feature = "crypto-big-mod-exp")]
105#[repr(C)]
106struct BigModExpParams {
107 base: *const u8,
108 base_len: u64,
109 exponent: *const u8,
110 exponent_len: u64,
111 modulus: *const u8,
112 modulus_len: u64,
113}
114
115#[cfg(any(
116 feature = "crypto-curve",
117 feature = "crypto-poseidon",
118 feature = "crypto-bn254",
119 feature = "crypto-big-mod-exp"
120))]
121#[inline]
122fn syscall_error(status: u64) -> ProgramError {
123 if status <= u32::MAX as u64 {
124 ProgramError::Custom(status as u32)
125 } else {
126 ProgramError::InvalidArgument
127 }
128}
129
130#[inline]
131pub fn sha256(inputs: &[&[u8]]) -> Result<Sha256Hash, ProgramError> {
132 if inputs.len() > MAX_HASH_SEGMENTS {
133 return Err(ProgramError::InvalidArgument);
134 }
135
136 let mut result = [0u8; 32];
137 unsafe {
140 crate::syscalls::sol_sha256(
141 inputs as *const _ as *const u8,
142 inputs.len() as u64,
143 result.as_mut_ptr(),
144 );
145 }
146 Ok(result)
147}
148
149#[inline]
150pub fn sha256_single(input: &[u8]) -> Result<Sha256Hash, ProgramError> {
151 sha256(&[input])
152}
153
154#[inline]
155pub fn keccak256(inputs: &[&[u8]]) -> Result<Keccak256Hash, ProgramError> {
156 if inputs.len() > MAX_HASH_SEGMENTS {
157 return Err(ProgramError::InvalidArgument);
158 }
159
160 let mut result = [0u8; 32];
161 unsafe {
164 crate::syscalls::sol_keccak256(
165 inputs as *const _ as *const u8,
166 inputs.len() as u64,
167 result.as_mut_ptr(),
168 );
169 }
170 Ok(result)
171}
172
173#[inline]
174pub fn keccak256_single(input: &[u8]) -> Result<Keccak256Hash, ProgramError> {
175 keccak256(&[input])
176}
177
178#[inline]
179pub fn blake3(inputs: &[&[u8]]) -> Result<Blake3Hash, ProgramError> {
180 if inputs.len() > MAX_HASH_SEGMENTS {
181 return Err(ProgramError::InvalidArgument);
182 }
183
184 let mut result = [0u8; 32];
185 let rc = unsafe {
188 crate::syscalls::sol_blake3(
189 inputs as *const _ as *const u8,
190 inputs.len() as u64,
191 result.as_mut_ptr(),
192 )
193 };
194 if rc != 0 {
195 return Err(ProgramError::InvalidArgument);
196 }
197 Ok(result)
198}
199
200#[inline]
201pub fn blake3_single(input: &[u8]) -> Result<Blake3Hash, ProgramError> {
202 blake3(&[input])
203}
204
205#[inline]
206pub fn secp256k1_recover(
207 message_hash: &[u8; 32],
208 recovery_id: u8,
209 signature: &[u8; 64],
210) -> Result<Secp256k1Pubkey, ProgramError> {
211 let mut result = [0u8; 64];
212 let rc = unsafe {
215 crate::syscalls::sol_secp256k1_recover(
216 message_hash.as_ptr(),
217 recovery_id as u64,
218 signature.as_ptr(),
219 result.as_mut_ptr(),
220 )
221 };
222 if rc != 0 {
223 return Err(ProgramError::InvalidArgument);
224 }
225 Ok(result)
226}
227
228#[inline]
229pub fn recover_ethereum_address(
230 message_hash: &[u8; 32],
231 recovery_id: u8,
232 signature: &[u8; 64],
233) -> Result<EthereumAddress, ProgramError> {
234 let pubkey = secp256k1_recover(message_hash, recovery_id, signature)?;
235 let digest = keccak256(&[&pubkey])?;
236 let mut address = [0u8; 20];
237 address.copy_from_slice(&digest[12..32]);
238 Ok(address)
239}
240
241#[inline]
248pub fn curve_validate_point(curve_id: u64, point: &[u8; 32]) -> Result<bool, ProgramError> {
249 #[cfg(not(target_os = "solana"))]
250 if curve_id == CURVE25519_EDWARDS {
251 return Ok(hopper_native::curve25519::is_on_curve(point));
252 }
253 let rc = unsafe {
256 crate::syscalls::sol_curve_validate_point(curve_id, point.as_ptr(), core::ptr::null_mut())
257 };
258 Ok(rc == 0)
259}
260
261#[inline]
262pub fn curve25519_edwards_validate_point(point: &[u8; 32]) -> Result<bool, ProgramError> {
263 curve_validate_point(CURVE25519_EDWARDS, point)
264}
265
266#[cfg(feature = "crypto-curve")]
267#[inline]
268fn curve_group_op(
269 curve_id: u64,
270 group_op: u64,
271 left: &CurvePoint,
272 right: &CurvePoint,
273) -> Result<CurvePoint, ProgramError> {
274 let mut result = [0u8; 32];
275 let rc = unsafe {
277 crate::syscalls::sol_curve_group_op(
278 curve_id,
279 group_op,
280 left.as_ptr(),
281 right.as_ptr(),
282 result.as_mut_ptr(),
283 )
284 };
285 if rc != 0 {
286 return Err(syscall_error(rc));
287 }
288 Ok(result)
289}
290
291#[cfg(feature = "crypto-curve")]
292#[inline]
293pub fn curve_group_add(
294 curve_id: u64,
295 left: &CurvePoint,
296 right: &CurvePoint,
297) -> Result<CurvePoint, ProgramError> {
298 curve_group_op(curve_id, CURVE_GROUP_ADD, left, right)
299}
300
301#[cfg(feature = "crypto-curve")]
302#[inline]
303pub fn curve_group_sub(
304 curve_id: u64,
305 left: &CurvePoint,
306 right: &CurvePoint,
307) -> Result<CurvePoint, ProgramError> {
308 curve_group_op(curve_id, CURVE_GROUP_SUB, left, right)
309}
310
311#[cfg(feature = "crypto-curve")]
312#[inline]
313pub fn curve_group_mul(
314 curve_id: u64,
315 scalar: &CurveScalar,
316 point: &CurvePoint,
317) -> Result<CurvePoint, ProgramError> {
318 let mut result = [0u8; 32];
319 let rc = unsafe {
322 crate::syscalls::sol_curve_group_op(
323 curve_id,
324 CURVE_GROUP_MUL,
325 scalar.as_ptr(),
326 point.as_ptr(),
327 result.as_mut_ptr(),
328 )
329 };
330 if rc != 0 {
331 return Err(syscall_error(rc));
332 }
333 Ok(result)
334}
335
336#[cfg(feature = "crypto-curve")]
337#[inline]
338pub fn curve_multiscalar_mul(
339 curve_id: u64,
340 scalars: &[CurveScalar],
341 points: &[CurvePoint],
342) -> Result<CurvePoint, ProgramError> {
343 if scalars.len() != points.len() || points.is_empty() {
344 return Err(ProgramError::InvalidArgument);
345 }
346
347 let mut result = [0u8; 32];
348 let rc = unsafe {
351 crate::syscalls::sol_curve_multiscalar_mul(
352 curve_id,
353 scalars.as_ptr() as *const u8,
354 points.as_ptr() as *const u8,
355 points.len() as u64,
356 result.as_mut_ptr(),
357 )
358 };
359 if rc != 0 {
360 return Err(syscall_error(rc));
361 }
362 Ok(result)
363}
364
365#[cfg(feature = "crypto-poseidon")]
366#[inline]
367pub fn poseidon_hashv(
368 parameters: u64,
369 endianness: u64,
370 inputs: &[&[u8]],
371) -> Result<PoseidonHash, ProgramError> {
372 if inputs.is_empty() || inputs.len() > MAX_POSEIDON_INPUTS {
373 return Err(ProgramError::InvalidArgument);
374 }
375 if inputs.iter().any(|input| input.len() != POSEIDON_INPUT_LEN) {
376 return Err(ProgramError::InvalidArgument);
377 }
378
379 let mut result = [0u8; 32];
380 let rc = unsafe {
383 crate::syscalls::sol_poseidon(
384 parameters,
385 endianness,
386 inputs as *const _ as *const u8,
387 inputs.len() as u64,
388 result.as_mut_ptr(),
389 )
390 };
391 if rc != 0 {
392 return Err(syscall_error(rc));
393 }
394 Ok(result)
395}
396
397#[cfg(feature = "crypto-poseidon")]
398#[inline]
399pub fn poseidon_hash(
400 parameters: u64,
401 endianness: u64,
402 input: &[u8; 32],
403) -> Result<PoseidonHash, ProgramError> {
404 poseidon_hashv(parameters, endianness, &[input])
405}
406
407#[cfg(feature = "crypto-poseidon")]
408#[inline]
409pub fn poseidon_bn254_x5(inputs: &[&[u8]]) -> Result<PoseidonHash, ProgramError> {
410 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, inputs)
411}
412
413#[cfg(feature = "crypto-bn254")]
414#[inline]
415fn alt_bn128_group_op<const OUT: usize>(
416 group_op: u64,
417 input: &[u8],
418) -> Result<[u8; OUT], ProgramError> {
419 let mut result = [0u8; OUT];
420 let rc = unsafe {
422 crate::syscalls::sol_alt_bn128_group_op(
423 group_op,
424 input.as_ptr(),
425 input.len() as u64,
426 result.as_mut_ptr(),
427 )
428 };
429 if rc != 0 {
430 return Err(syscall_error(rc));
431 }
432 Ok(result)
433}
434
435#[cfg(feature = "crypto-bn254")]
436#[inline]
437fn alt_bn128_compression_op<const OUT: usize>(
438 op: u64,
439 input: &[u8],
440) -> Result<[u8; OUT], ProgramError> {
441 let mut result = [0u8; OUT];
442 let rc = unsafe {
444 crate::syscalls::sol_alt_bn128_compression(
445 op,
446 input.as_ptr(),
447 input.len() as u64,
448 result.as_mut_ptr(),
449 )
450 };
451 if rc != 0 {
452 return Err(syscall_error(rc));
453 }
454 Ok(result)
455}
456
457#[cfg(feature = "crypto-bn254")]
458#[inline]
459pub fn alt_bn128_g1_addition_be(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
460 if input.len() > ALT_BN128_G1_ADDITION_INPUT_SIZE {
461 return Err(ProgramError::InvalidArgument);
462 }
463 alt_bn128_group_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_ADD_BE, input)
464}
465
466#[cfg(feature = "crypto-bn254")]
467#[inline]
468pub fn alt_bn128_g1_multiplication_be(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
469 if input.len() > ALT_BN128_G1_MULTIPLICATION_INPUT_SIZE {
470 return Err(ProgramError::InvalidArgument);
471 }
472 alt_bn128_group_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_MUL_BE, input)
473}
474
475#[cfg(feature = "crypto-bn254")]
476#[inline]
477pub fn alt_bn128_pairing_be(input: &[u8]) -> Result<AltBn128PairingResult, ProgramError> {
478 if !input.len().is_multiple_of(ALT_BN128_PAIRING_ELEMENT_SIZE) {
479 return Err(ProgramError::InvalidArgument);
480 }
481 alt_bn128_group_op::<ALT_BN128_FIELD_SIZE>(ALT_BN128_PAIRING_BE, input)
482}
483
484#[cfg(feature = "crypto-bn254")]
485#[inline]
486pub fn alt_bn128_add(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
487 alt_bn128_g1_addition_be(input)
488}
489
490#[cfg(feature = "crypto-bn254")]
491#[inline]
492pub fn alt_bn128_mul(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
493 alt_bn128_g1_multiplication_be(input)
494}
495
496#[cfg(feature = "crypto-bn254")]
497#[inline]
498pub fn alt_bn128_pairing(input: &[u8]) -> Result<AltBn128PairingResult, ProgramError> {
499 alt_bn128_pairing_be(input)
500}
501
502#[cfg(feature = "crypto-bn254")]
503#[inline]
504pub fn alt_bn128_g1_compress_be(input: &AltBn128G1) -> Result<AltBn128G1Compressed, ProgramError> {
505 alt_bn128_compression_op::<ALT_BN128_FIELD_SIZE>(ALT_BN128_G1_COMPRESS_BE, input)
506}
507
508#[cfg(feature = "crypto-bn254")]
509#[inline]
510pub fn alt_bn128_g1_decompress_be(
511 input: &AltBn128G1Compressed,
512) -> Result<AltBn128G1, ProgramError> {
513 alt_bn128_compression_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_DECOMPRESS_BE, input)
514}
515
516#[cfg(feature = "crypto-bn254")]
517#[inline]
518pub fn alt_bn128_g2_compress_be(input: &AltBn128G2) -> Result<AltBn128G2Compressed, ProgramError> {
519 alt_bn128_compression_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G2_COMPRESS_BE, input)
520}
521
522#[cfg(feature = "crypto-bn254")]
523#[inline]
524pub fn alt_bn128_g2_decompress_be(
525 input: &AltBn128G2Compressed,
526) -> Result<AltBn128G2, ProgramError> {
527 alt_bn128_compression_op::<ALT_BN128_G2_POINT_SIZE>(ALT_BN128_G2_DECOMPRESS_BE, input)
528}
529
530#[cfg(feature = "crypto-big-mod-exp")]
531#[inline]
532pub fn big_mod_exp(
533 base: &[u8],
534 exponent: &[u8],
535 modulus: &[u8],
536 output: &mut [u8],
537) -> Result<(), ProgramError> {
538 if modulus.is_empty() || output.len() != modulus.len() {
539 return Err(ProgramError::InvalidArgument);
540 }
541
542 let params = BigModExpParams {
543 base: base.as_ptr(),
544 base_len: base.len() as u64,
545 exponent: exponent.as_ptr(),
546 exponent_len: exponent.len() as u64,
547 modulus: modulus.as_ptr(),
548 modulus_len: modulus.len() as u64,
549 };
550 let rc = unsafe {
553 crate::syscalls::sol_big_mod_exp(
554 ¶ms as *const BigModExpParams as *const u8,
555 output.as_mut_ptr(),
556 )
557 };
558 if rc != 0 {
559 return Err(syscall_error(rc));
560 }
561 Ok(())
562}
563
564#[inline(always)]
565pub fn get_stack_height() -> u64 {
566 crate::syscalls::sol_get_stack_height()
567}
568
569#[inline(always)]
570pub fn is_top_level() -> bool {
571 get_stack_height() <= 1
572}
573
574#[inline(always)]
575pub fn is_cpi() -> bool {
576 get_stack_height() > 1
577}
578
579#[inline(always)]
580pub fn require_top_level() -> Result<(), ProgramError> {
581 if is_top_level() {
582 Ok(())
583 } else {
584 Err(ProgramError::InvalidArgument)
585 }
586}
587
588#[inline]
593pub fn get_processed_instruction_into<'a>(
594 index: u64,
595 data: &'a mut [u8],
596 accounts: &'a mut [ProcessedInstructionAccount],
597) -> Result<Option<ProcessedInstructionView<'a>>, ProgramError> {
598 hopper_native::introspect::get_processed_instruction_into(index, data, accounts)
599 .map(|view| {
600 view.map(|view| ProcessedInstructionView {
601 program_id: view.program_id.into(),
602 data: view.data,
603 accounts: view.accounts,
604 })
605 })
606 .map_err(ProgramError::from)
607}
608
609#[inline]
612pub fn get_processed_instruction(index: u64) -> Option<ProcessedInstruction> {
613 let view = hopper_native::introspect::get_processed_instruction(index)?;
614 Some(ProcessedInstruction {
615 program_id: view.program_id.into(),
616 data: view.data,
617 data_len: view.data_len,
618 accounts_len: view.accounts_len,
619 })
620}
621
622#[inline]
627pub fn get_processed_instruction_data<const MAX_DATA: usize>(
628 index: u64,
629) -> Option<ProcessedInstructionData<MAX_DATA>> {
630 let mut data = [0; MAX_DATA];
631 let mut accounts = core::array::from_fn::<_, 64, _>(|_| ProcessedInstructionAccount::default());
632 let view = get_processed_instruction_into(index, &mut data, &mut accounts).ok()??;
633 let program_id = view.program_id;
634 let data_len = view.data.len();
635 Some(ProcessedInstructionData {
636 program_id,
637 data,
638 data_len,
639 })
640}
641
642#[inline]
645pub fn require_ed25519_instruction(
646 sibling_index: u64,
647) -> Result<ProcessedInstruction, ProgramError> {
648 let instruction =
649 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
650 if instruction.program_id != ED25519_PROGRAM_ID {
651 return Err(ProgramError::IncorrectProgramId);
652 }
653 Ok(instruction)
654}
655
656#[inline]
659pub fn require_ed25519_instruction_data<const MAX_DATA: usize>(
660 sibling_index: u64,
661) -> Result<ProcessedInstructionData<MAX_DATA>, ProgramError> {
662 let instruction =
663 get_processed_instruction_data(sibling_index).ok_or(ProgramError::InvalidArgument)?;
664 if instruction.program_id != ED25519_PROGRAM_ID {
665 return Err(ProgramError::IncorrectProgramId);
666 }
667 Ok(instruction)
668}
669
670#[inline]
672pub fn require_secp256k1_instruction(
673 sibling_index: u64,
674) -> Result<ProcessedInstruction, ProgramError> {
675 let instruction =
676 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
677 if instruction.program_id != SECP256K1_PROGRAM_ID {
678 return Err(ProgramError::IncorrectProgramId);
679 }
680 Ok(instruction)
681}
682
683#[inline]
687pub fn require_secp256r1_instruction(
688 sibling_index: u64,
689) -> Result<ProcessedInstruction, ProgramError> {
690 let instruction =
691 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
692 if instruction.program_id != SECP256R1_PROGRAM_ID {
693 return Err(ProgramError::IncorrectProgramId);
694 }
695 Ok(instruction)
696}
697
698#[inline]
701pub fn require_secp256r1_instruction_data<const MAX_DATA: usize>(
702 sibling_index: u64,
703) -> Result<ProcessedInstructionData<MAX_DATA>, ProgramError> {
704 let instruction =
705 get_processed_instruction_data(sibling_index).ok_or(ProgramError::InvalidArgument)?;
706 if instruction.program_id != SECP256R1_PROGRAM_ID {
707 return Err(ProgramError::IncorrectProgramId);
708 }
709 Ok(instruction)
710}
711
712#[cfg(test)]
713mod tests {
714 use super::*;
715
716 const EMPTY: &[u8] = b"";
717
718 #[test]
719 fn host_without_a_trace_has_no_processed_siblings() {
720 assert!(get_processed_instruction(0).is_none());
721 assert!(get_processed_instruction_data::<16>(0).is_none());
722 assert!(get_processed_instruction_into(0, &mut [], &mut [])
723 .unwrap()
724 .is_none());
725 assert_eq!(
726 require_ed25519_instruction(0).unwrap_err(),
727 ProgramError::InvalidArgument
728 );
729 }
730
731 #[test]
732 fn hash_helpers_accept_sixteen_segments() {
733 let inputs = [EMPTY; MAX_HASH_SEGMENTS];
734
735 assert!(sha256(&inputs).is_ok());
736 assert!(keccak256(&inputs).is_ok());
737 assert!(blake3(&inputs).is_ok());
738 }
739
740 #[test]
741 fn hash_helpers_reject_more_than_sixteen_segments() {
742 let inputs = [EMPTY; MAX_HASH_SEGMENTS + 1];
743
744 assert_eq!(sha256(&inputs), Err(ProgramError::InvalidArgument));
745 assert_eq!(keccak256(&inputs), Err(ProgramError::InvalidArgument));
746 assert_eq!(blake3(&inputs), Err(ProgramError::InvalidArgument));
747 }
748
749 #[cfg(feature = "crypto-curve")]
750 #[test]
751 fn curve_msm_requires_matching_nonempty_inputs() {
752 let scalars = [[0u8; 32]; 2];
753 let points = [[0u8; 32]; 1];
754
755 assert_eq!(
756 curve_multiscalar_mul(CURVE25519_EDWARDS, &scalars, &points),
757 Err(ProgramError::InvalidArgument)
758 );
759 assert_eq!(
760 curve_multiscalar_mul(CURVE25519_EDWARDS, &[], &[]),
761 Err(ProgramError::InvalidArgument)
762 );
763 }
764
765 #[cfg(feature = "crypto-poseidon")]
766 #[test]
767 fn poseidon_rejects_bad_input_shape() {
768 let short = [0u8; 31];
769 let input = [0u8; 32];
770 let too_many = [&input[..]; MAX_POSEIDON_INPUTS + 1];
771
772 assert_eq!(
773 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &[]),
774 Err(ProgramError::InvalidArgument)
775 );
776 assert_eq!(
777 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &[&short]),
778 Err(ProgramError::InvalidArgument)
779 );
780 assert_eq!(
781 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &too_many),
782 Err(ProgramError::InvalidArgument)
783 );
784 }
785
786 #[cfg(feature = "crypto-bn254")]
787 #[test]
788 fn bn254_rejects_bad_input_lengths() {
789 let oversized_add = [0u8; ALT_BN128_G1_ADDITION_INPUT_SIZE + 1];
790 let bad_pairing = [0u8; ALT_BN128_PAIRING_ELEMENT_SIZE + 1];
791
792 assert_eq!(
793 alt_bn128_g1_addition_be(&oversized_add),
794 Err(ProgramError::InvalidArgument)
795 );
796 assert_eq!(
797 alt_bn128_pairing_be(&bad_pairing),
798 Err(ProgramError::InvalidArgument)
799 );
800 }
801
802 #[cfg(feature = "crypto-big-mod-exp")]
803 #[test]
804 fn big_mod_exp_requires_output_matching_modulus() {
805 let mut output = [0u8; 1];
806 let mut empty_output = [];
807
808 assert_eq!(
809 big_mod_exp(&[1], &[1], &[1, 2], &mut output),
810 Err(ProgramError::InvalidArgument)
811 );
812 assert_eq!(
813 big_mod_exp(&[1], &[1], &[], &mut empty_output),
814 Err(ProgramError::InvalidArgument)
815 );
816 }
817}