spl_discriminator/
lib.rs

1//! Crate defining a discriminator type, which creates a set of bytes
2//! meant to be unique for instructions or struct types
3
4#![deny(missing_docs)]
5#![cfg_attr(not(test), forbid(unsafe_code))]
6
7extern crate self as spl_discriminator;
8
9/// Exports the discriminator module
10pub mod discriminator;
11
12// Export for downstream
13pub use {
14    discriminator::{ArrayDiscriminator, SplDiscriminate},
15    spl_discriminator_derive::SplDiscriminate,
16};
17
18#[cfg(test)]
19mod tests {
20    use {super::*, crate::discriminator::ArrayDiscriminator};
21
22    #[allow(dead_code)]
23    #[derive(SplDiscriminate)]
24    #[discriminator_hash_input("my_first_instruction")]
25    pub struct MyInstruction1 {
26        arg1: String,
27        arg2: u8,
28    }
29
30    #[allow(dead_code)]
31    #[derive(SplDiscriminate)]
32    #[discriminator_hash_input("global:my_second_instruction")]
33    pub enum MyInstruction2 {
34        One,
35        Two,
36        Three,
37    }
38
39    #[allow(dead_code)]
40    #[derive(SplDiscriminate)]
41    #[discriminator_hash_input("global:my_instruction_with_lifetime")]
42    pub struct MyInstruction3<'a> {
43        data: &'a [u8],
44    }
45
46    #[allow(dead_code)]
47    #[derive(SplDiscriminate)]
48    #[discriminator_hash_input("global:my_instruction_with_one_generic")]
49    pub struct MyInstruction4<T> {
50        data: T,
51    }
52
53    #[allow(dead_code)]
54    #[derive(SplDiscriminate)]
55    #[discriminator_hash_input("global:my_instruction_with_one_generic_and_lifetime")]
56    pub struct MyInstruction5<'b, T> {
57        data: &'b [T],
58    }
59
60    #[allow(dead_code)]
61    #[derive(SplDiscriminate)]
62    #[discriminator_hash_input("global:my_instruction_with_multiple_generics_and_lifetime")]
63    pub struct MyInstruction6<'c, U, V> {
64        data1: &'c [U],
65        data2: &'c [V],
66    }
67
68    #[allow(dead_code)]
69    #[derive(SplDiscriminate)]
70    #[discriminator_hash_input(
71        "global:my_instruction_with_multiple_generics_and_lifetime_and_where"
72    )]
73    pub struct MyInstruction7<'c, U, V>
74    where
75        U: Clone + Copy,
76        V: Clone + Copy,
77    {
78        data1: &'c [U],
79        data2: &'c [V],
80    }
81
82    fn assert_discriminator<T: spl_discriminator::discriminator::SplDiscriminate>(
83        hash_input: &str,
84    ) {
85        let discriminator = build_discriminator(hash_input);
86        assert_eq!(
87            T::SPL_DISCRIMINATOR,
88            discriminator,
89            "Discriminator mismatch: case: {}",
90            hash_input
91        );
92        assert_eq!(
93            T::SPL_DISCRIMINATOR_SLICE,
94            discriminator.as_slice(),
95            "Discriminator mismatch: case: {}",
96            hash_input
97        );
98    }
99
100    fn build_discriminator(hash_input: &str) -> ArrayDiscriminator {
101        let preimage = miraland_program::hash::hashv(&[hash_input.as_bytes()]);
102        let mut bytes = [0u8; 8];
103        bytes.copy_from_slice(&preimage.to_bytes()[..8]);
104        ArrayDiscriminator::new(bytes)
105    }
106
107    #[test]
108    fn test_discrminators() {
109        let runtime_discrim = ArrayDiscriminator::new_with_hash_input("my_runtime_hash_input");
110        assert_eq!(
111            runtime_discrim,
112            build_discriminator("my_runtime_hash_input"),
113        );
114
115        assert_discriminator::<MyInstruction1>("my_first_instruction");
116        assert_discriminator::<MyInstruction2>("global:my_second_instruction");
117        assert_discriminator::<MyInstruction3<'_>>("global:my_instruction_with_lifetime");
118        assert_discriminator::<MyInstruction4<u8>>("global:my_instruction_with_one_generic");
119        assert_discriminator::<MyInstruction5<'_, u8>>(
120            "global:my_instruction_with_one_generic_and_lifetime",
121        );
122        assert_discriminator::<MyInstruction6<'_, u8, u8>>(
123            "global:my_instruction_with_multiple_generics_and_lifetime",
124        );
125        assert_discriminator::<MyInstruction7<'_, u8, u8>>(
126            "global:my_instruction_with_multiple_generics_and_lifetime_and_where",
127        );
128    }
129}
130
131#[cfg(all(test, feature = "borsh"))]
132mod borsh_test {
133    use super::*;
134
135    #[test]
136    fn borsh_test() {
137        let my_discrim = ArrayDiscriminator::new_with_hash_input("my_discrim");
138        let mut buffer = [0u8; 8];
139        my_discrim.serialize(&mut buffer[..]).unwrap();
140        let my_discrim_again = ArrayDiscriminator::try_from_slice(&buffer).unwrap();
141        assert_eq!(my_discrim, my_discrim_again);
142        assert_eq!(buf, my_discrim.into());
143    }
144}