Skip to main content

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