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]
242pub fn curve_validate_point(curve_id: u64, point: &[u8; 32]) -> Result<bool, ProgramError> {
243 let rc = unsafe {
246 crate::syscalls::sol_curve_validate_point(curve_id, point.as_ptr(), core::ptr::null_mut())
247 };
248 Ok(rc == 0)
249}
250
251#[inline]
252pub fn curve25519_edwards_validate_point(point: &[u8; 32]) -> Result<bool, ProgramError> {
253 curve_validate_point(CURVE25519_EDWARDS, point)
254}
255
256#[cfg(feature = "crypto-curve")]
257#[inline]
258fn curve_group_op(
259 curve_id: u64,
260 group_op: u64,
261 left: &CurvePoint,
262 right: &CurvePoint,
263) -> Result<CurvePoint, ProgramError> {
264 let mut result = [0u8; 32];
265 let rc = unsafe {
267 crate::syscalls::sol_curve_group_op(
268 curve_id,
269 group_op,
270 left.as_ptr(),
271 right.as_ptr(),
272 result.as_mut_ptr(),
273 )
274 };
275 if rc != 0 {
276 return Err(syscall_error(rc));
277 }
278 Ok(result)
279}
280
281#[cfg(feature = "crypto-curve")]
282#[inline]
283pub fn curve_group_add(
284 curve_id: u64,
285 left: &CurvePoint,
286 right: &CurvePoint,
287) -> Result<CurvePoint, ProgramError> {
288 curve_group_op(curve_id, CURVE_GROUP_ADD, left, right)
289}
290
291#[cfg(feature = "crypto-curve")]
292#[inline]
293pub fn curve_group_sub(
294 curve_id: u64,
295 left: &CurvePoint,
296 right: &CurvePoint,
297) -> Result<CurvePoint, ProgramError> {
298 curve_group_op(curve_id, CURVE_GROUP_SUB, left, right)
299}
300
301#[cfg(feature = "crypto-curve")]
302#[inline]
303pub fn curve_group_mul(
304 curve_id: u64,
305 scalar: &CurveScalar,
306 point: &CurvePoint,
307) -> Result<CurvePoint, ProgramError> {
308 let mut result = [0u8; 32];
309 let rc = unsafe {
312 crate::syscalls::sol_curve_group_op(
313 curve_id,
314 CURVE_GROUP_MUL,
315 scalar.as_ptr(),
316 point.as_ptr(),
317 result.as_mut_ptr(),
318 )
319 };
320 if rc != 0 {
321 return Err(syscall_error(rc));
322 }
323 Ok(result)
324}
325
326#[cfg(feature = "crypto-curve")]
327#[inline]
328pub fn curve_multiscalar_mul(
329 curve_id: u64,
330 scalars: &[CurveScalar],
331 points: &[CurvePoint],
332) -> Result<CurvePoint, ProgramError> {
333 if scalars.len() != points.len() || points.is_empty() {
334 return Err(ProgramError::InvalidArgument);
335 }
336
337 let mut result = [0u8; 32];
338 let rc = unsafe {
341 crate::syscalls::sol_curve_multiscalar_mul(
342 curve_id,
343 scalars.as_ptr() as *const u8,
344 points.as_ptr() as *const u8,
345 points.len() as u64,
346 result.as_mut_ptr(),
347 )
348 };
349 if rc != 0 {
350 return Err(syscall_error(rc));
351 }
352 Ok(result)
353}
354
355#[cfg(feature = "crypto-poseidon")]
356#[inline]
357pub fn poseidon_hashv(
358 parameters: u64,
359 endianness: u64,
360 inputs: &[&[u8]],
361) -> Result<PoseidonHash, ProgramError> {
362 if inputs.is_empty() || inputs.len() > MAX_POSEIDON_INPUTS {
363 return Err(ProgramError::InvalidArgument);
364 }
365 if inputs.iter().any(|input| input.len() != POSEIDON_INPUT_LEN) {
366 return Err(ProgramError::InvalidArgument);
367 }
368
369 let mut result = [0u8; 32];
370 let rc = unsafe {
373 crate::syscalls::sol_poseidon(
374 parameters,
375 endianness,
376 inputs as *const _ as *const u8,
377 inputs.len() as u64,
378 result.as_mut_ptr(),
379 )
380 };
381 if rc != 0 {
382 return Err(syscall_error(rc));
383 }
384 Ok(result)
385}
386
387#[cfg(feature = "crypto-poseidon")]
388#[inline]
389pub fn poseidon_hash(
390 parameters: u64,
391 endianness: u64,
392 input: &[u8; 32],
393) -> Result<PoseidonHash, ProgramError> {
394 poseidon_hashv(parameters, endianness, &[input])
395}
396
397#[cfg(feature = "crypto-poseidon")]
398#[inline]
399pub fn poseidon_bn254_x5(inputs: &[&[u8]]) -> Result<PoseidonHash, ProgramError> {
400 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, inputs)
401}
402
403#[cfg(feature = "crypto-bn254")]
404#[inline]
405fn alt_bn128_group_op<const OUT: usize>(
406 group_op: u64,
407 input: &[u8],
408) -> Result<[u8; OUT], ProgramError> {
409 let mut result = [0u8; OUT];
410 let rc = unsafe {
412 crate::syscalls::sol_alt_bn128_group_op(
413 group_op,
414 input.as_ptr(),
415 input.len() as u64,
416 result.as_mut_ptr(),
417 )
418 };
419 if rc != 0 {
420 return Err(syscall_error(rc));
421 }
422 Ok(result)
423}
424
425#[cfg(feature = "crypto-bn254")]
426#[inline]
427fn alt_bn128_compression_op<const OUT: usize>(
428 op: u64,
429 input: &[u8],
430) -> Result<[u8; OUT], ProgramError> {
431 let mut result = [0u8; OUT];
432 let rc = unsafe {
434 crate::syscalls::sol_alt_bn128_compression(
435 op,
436 input.as_ptr(),
437 input.len() as u64,
438 result.as_mut_ptr(),
439 )
440 };
441 if rc != 0 {
442 return Err(syscall_error(rc));
443 }
444 Ok(result)
445}
446
447#[cfg(feature = "crypto-bn254")]
448#[inline]
449pub fn alt_bn128_g1_addition_be(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
450 if input.len() > ALT_BN128_G1_ADDITION_INPUT_SIZE {
451 return Err(ProgramError::InvalidArgument);
452 }
453 alt_bn128_group_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_ADD_BE, input)
454}
455
456#[cfg(feature = "crypto-bn254")]
457#[inline]
458pub fn alt_bn128_g1_multiplication_be(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
459 if input.len() > ALT_BN128_G1_MULTIPLICATION_INPUT_SIZE {
460 return Err(ProgramError::InvalidArgument);
461 }
462 alt_bn128_group_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_MUL_BE, input)
463}
464
465#[cfg(feature = "crypto-bn254")]
466#[inline]
467pub fn alt_bn128_pairing_be(input: &[u8]) -> Result<AltBn128PairingResult, ProgramError> {
468 if !input.len().is_multiple_of(ALT_BN128_PAIRING_ELEMENT_SIZE) {
469 return Err(ProgramError::InvalidArgument);
470 }
471 alt_bn128_group_op::<ALT_BN128_FIELD_SIZE>(ALT_BN128_PAIRING_BE, input)
472}
473
474#[cfg(feature = "crypto-bn254")]
475#[inline]
476pub fn alt_bn128_add(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
477 alt_bn128_g1_addition_be(input)
478}
479
480#[cfg(feature = "crypto-bn254")]
481#[inline]
482pub fn alt_bn128_mul(input: &[u8]) -> Result<AltBn128G1, ProgramError> {
483 alt_bn128_g1_multiplication_be(input)
484}
485
486#[cfg(feature = "crypto-bn254")]
487#[inline]
488pub fn alt_bn128_pairing(input: &[u8]) -> Result<AltBn128PairingResult, ProgramError> {
489 alt_bn128_pairing_be(input)
490}
491
492#[cfg(feature = "crypto-bn254")]
493#[inline]
494pub fn alt_bn128_g1_compress_be(input: &AltBn128G1) -> Result<AltBn128G1Compressed, ProgramError> {
495 alt_bn128_compression_op::<ALT_BN128_FIELD_SIZE>(ALT_BN128_G1_COMPRESS_BE, input)
496}
497
498#[cfg(feature = "crypto-bn254")]
499#[inline]
500pub fn alt_bn128_g1_decompress_be(
501 input: &AltBn128G1Compressed,
502) -> Result<AltBn128G1, ProgramError> {
503 alt_bn128_compression_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G1_DECOMPRESS_BE, input)
504}
505
506#[cfg(feature = "crypto-bn254")]
507#[inline]
508pub fn alt_bn128_g2_compress_be(input: &AltBn128G2) -> Result<AltBn128G2Compressed, ProgramError> {
509 alt_bn128_compression_op::<ALT_BN128_G1_POINT_SIZE>(ALT_BN128_G2_COMPRESS_BE, input)
510}
511
512#[cfg(feature = "crypto-bn254")]
513#[inline]
514pub fn alt_bn128_g2_decompress_be(
515 input: &AltBn128G2Compressed,
516) -> Result<AltBn128G2, ProgramError> {
517 alt_bn128_compression_op::<ALT_BN128_G2_POINT_SIZE>(ALT_BN128_G2_DECOMPRESS_BE, input)
518}
519
520#[cfg(feature = "crypto-big-mod-exp")]
521#[inline]
522pub fn big_mod_exp(
523 base: &[u8],
524 exponent: &[u8],
525 modulus: &[u8],
526 output: &mut [u8],
527) -> Result<(), ProgramError> {
528 if modulus.is_empty() || output.len() != modulus.len() {
529 return Err(ProgramError::InvalidArgument);
530 }
531
532 let params = BigModExpParams {
533 base: base.as_ptr(),
534 base_len: base.len() as u64,
535 exponent: exponent.as_ptr(),
536 exponent_len: exponent.len() as u64,
537 modulus: modulus.as_ptr(),
538 modulus_len: modulus.len() as u64,
539 };
540 let rc = unsafe {
543 crate::syscalls::sol_big_mod_exp(
544 ¶ms as *const BigModExpParams as *const u8,
545 output.as_mut_ptr(),
546 )
547 };
548 if rc != 0 {
549 return Err(syscall_error(rc));
550 }
551 Ok(())
552}
553
554#[inline(always)]
555pub fn get_stack_height() -> u64 {
556 crate::syscalls::sol_get_stack_height()
557}
558
559#[inline(always)]
560pub fn is_top_level() -> bool {
561 get_stack_height() <= 1
562}
563
564#[inline(always)]
565pub fn is_cpi() -> bool {
566 get_stack_height() > 1
567}
568
569#[inline(always)]
570pub fn require_top_level() -> Result<(), ProgramError> {
571 if is_top_level() {
572 Ok(())
573 } else {
574 Err(ProgramError::InvalidArgument)
575 }
576}
577
578#[inline]
583pub fn get_processed_instruction_into<'a>(
584 index: u64,
585 data: &'a mut [u8],
586 accounts: &'a mut [ProcessedInstructionAccount],
587) -> Result<Option<ProcessedInstructionView<'a>>, ProgramError> {
588 hopper_native::introspect::get_processed_instruction_into(index, data, accounts)
589 .map(|view| {
590 view.map(|view| ProcessedInstructionView {
591 program_id: view.program_id.into(),
592 data: view.data,
593 accounts: view.accounts,
594 })
595 })
596 .map_err(ProgramError::from)
597}
598
599#[inline]
602pub fn get_processed_instruction(index: u64) -> Option<ProcessedInstruction> {
603 let view = hopper_native::introspect::get_processed_instruction(index)?;
604 Some(ProcessedInstruction {
605 program_id: view.program_id.into(),
606 data: view.data,
607 data_len: view.data_len,
608 accounts_len: view.accounts_len,
609 })
610}
611
612#[inline]
617pub fn get_processed_instruction_data<const MAX_DATA: usize>(
618 index: u64,
619) -> Option<ProcessedInstructionData<MAX_DATA>> {
620 let mut data = [0; MAX_DATA];
621 let mut accounts = core::array::from_fn::<_, 64, _>(|_| ProcessedInstructionAccount::default());
622 let view = get_processed_instruction_into(index, &mut data, &mut accounts).ok()??;
623 let program_id = view.program_id;
624 let data_len = view.data.len();
625 Some(ProcessedInstructionData {
626 program_id,
627 data,
628 data_len,
629 })
630}
631
632#[inline]
635pub fn require_ed25519_instruction(
636 sibling_index: u64,
637) -> Result<ProcessedInstruction, ProgramError> {
638 let instruction =
639 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
640 if instruction.program_id != ED25519_PROGRAM_ID {
641 return Err(ProgramError::IncorrectProgramId);
642 }
643 Ok(instruction)
644}
645
646#[inline]
649pub fn require_ed25519_instruction_data<const MAX_DATA: usize>(
650 sibling_index: u64,
651) -> Result<ProcessedInstructionData<MAX_DATA>, ProgramError> {
652 let instruction =
653 get_processed_instruction_data(sibling_index).ok_or(ProgramError::InvalidArgument)?;
654 if instruction.program_id != ED25519_PROGRAM_ID {
655 return Err(ProgramError::IncorrectProgramId);
656 }
657 Ok(instruction)
658}
659
660#[inline]
662pub fn require_secp256k1_instruction(
663 sibling_index: u64,
664) -> Result<ProcessedInstruction, ProgramError> {
665 let instruction =
666 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
667 if instruction.program_id != SECP256K1_PROGRAM_ID {
668 return Err(ProgramError::IncorrectProgramId);
669 }
670 Ok(instruction)
671}
672
673#[inline]
677pub fn require_secp256r1_instruction(
678 sibling_index: u64,
679) -> Result<ProcessedInstruction, ProgramError> {
680 let instruction =
681 get_processed_instruction(sibling_index).ok_or(ProgramError::InvalidArgument)?;
682 if instruction.program_id != SECP256R1_PROGRAM_ID {
683 return Err(ProgramError::IncorrectProgramId);
684 }
685 Ok(instruction)
686}
687
688#[inline]
691pub fn require_secp256r1_instruction_data<const MAX_DATA: usize>(
692 sibling_index: u64,
693) -> Result<ProcessedInstructionData<MAX_DATA>, ProgramError> {
694 let instruction =
695 get_processed_instruction_data(sibling_index).ok_or(ProgramError::InvalidArgument)?;
696 if instruction.program_id != SECP256R1_PROGRAM_ID {
697 return Err(ProgramError::IncorrectProgramId);
698 }
699 Ok(instruction)
700}
701
702#[cfg(test)]
703mod tests {
704 use super::*;
705
706 const EMPTY: &[u8] = b"";
707
708 #[test]
709 fn host_without_a_trace_has_no_processed_siblings() {
710 assert!(get_processed_instruction(0).is_none());
711 assert!(get_processed_instruction_data::<16>(0).is_none());
712 assert!(get_processed_instruction_into(0, &mut [], &mut [])
713 .unwrap()
714 .is_none());
715 assert_eq!(
716 require_ed25519_instruction(0).unwrap_err(),
717 ProgramError::InvalidArgument
718 );
719 }
720
721 #[test]
722 fn hash_helpers_accept_sixteen_segments() {
723 let inputs = [EMPTY; MAX_HASH_SEGMENTS];
724
725 assert!(sha256(&inputs).is_ok());
726 assert!(keccak256(&inputs).is_ok());
727 assert!(blake3(&inputs).is_ok());
728 }
729
730 #[test]
731 fn hash_helpers_reject_more_than_sixteen_segments() {
732 let inputs = [EMPTY; MAX_HASH_SEGMENTS + 1];
733
734 assert_eq!(sha256(&inputs), Err(ProgramError::InvalidArgument));
735 assert_eq!(keccak256(&inputs), Err(ProgramError::InvalidArgument));
736 assert_eq!(blake3(&inputs), Err(ProgramError::InvalidArgument));
737 }
738
739 #[cfg(feature = "crypto-curve")]
740 #[test]
741 fn curve_msm_requires_matching_nonempty_inputs() {
742 let scalars = [[0u8; 32]; 2];
743 let points = [[0u8; 32]; 1];
744
745 assert_eq!(
746 curve_multiscalar_mul(CURVE25519_EDWARDS, &scalars, &points),
747 Err(ProgramError::InvalidArgument)
748 );
749 assert_eq!(
750 curve_multiscalar_mul(CURVE25519_EDWARDS, &[], &[]),
751 Err(ProgramError::InvalidArgument)
752 );
753 }
754
755 #[cfg(feature = "crypto-poseidon")]
756 #[test]
757 fn poseidon_rejects_bad_input_shape() {
758 let short = [0u8; 31];
759 let input = [0u8; 32];
760 let too_many = [&input[..]; MAX_POSEIDON_INPUTS + 1];
761
762 assert_eq!(
763 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &[]),
764 Err(ProgramError::InvalidArgument)
765 );
766 assert_eq!(
767 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &[&short]),
768 Err(ProgramError::InvalidArgument)
769 );
770 assert_eq!(
771 poseidon_hashv(POSEIDON_BN254_X5, POSEIDON_BIG_ENDIAN, &too_many),
772 Err(ProgramError::InvalidArgument)
773 );
774 }
775
776 #[cfg(feature = "crypto-bn254")]
777 #[test]
778 fn bn254_rejects_bad_input_lengths() {
779 let oversized_add = [0u8; ALT_BN128_G1_ADDITION_INPUT_SIZE + 1];
780 let bad_pairing = [0u8; ALT_BN128_PAIRING_ELEMENT_SIZE + 1];
781
782 assert_eq!(
783 alt_bn128_g1_addition_be(&oversized_add),
784 Err(ProgramError::InvalidArgument)
785 );
786 assert_eq!(
787 alt_bn128_pairing_be(&bad_pairing),
788 Err(ProgramError::InvalidArgument)
789 );
790 }
791
792 #[cfg(feature = "crypto-big-mod-exp")]
793 #[test]
794 fn big_mod_exp_requires_output_matching_modulus() {
795 let mut output = [0u8; 1];
796 let mut empty_output = [];
797
798 assert_eq!(
799 big_mod_exp(&[1], &[1], &[1, 2], &mut output),
800 Err(ProgramError::InvalidArgument)
801 );
802 assert_eq!(
803 big_mod_exp(&[1], &[1], &[], &mut empty_output),
804 Err(ProgramError::InvalidArgument)
805 );
806 }
807}