1pub use equix;
2#[cfg(not(feature = "solana"))]
3use sha3::Digest;
4
5#[inline(always)]
7pub fn hash(challenge: &[u8; 32], nonce: &[u8; 8]) -> Result<Hash, DrillxError> {
8 let digest = digest(challenge, nonce)?;
9 Ok(Hash {
10 d: digest,
11 h: hashv(&digest, nonce),
12 })
13}
14
15#[inline(always)]
17pub fn hash_with_memory(
18 memory: &mut equix::SolverMemory,
19 challenge: &[u8; 32],
20 nonce: &[u8; 8],
21) -> Result<Hash, DrillxError> {
22 let digest = digest_with_memory(memory, challenge, nonce)?;
23 Ok(Hash {
24 d: digest,
25 h: hashv(&digest, nonce),
26 })
27}
28
29#[inline(always)]
31pub fn seed(challenge: &[u8; 32], nonce: &[u8; 8]) -> [u8; 40] {
32 let mut result = [0; 40];
33 result[..32].copy_from_slice(challenge);
34 result[32..].copy_from_slice(nonce);
35 result
36}
37
38#[inline(always)]
40fn digest(challenge: &[u8; 32], nonce: &[u8; 8]) -> Result<[u8; 16], DrillxError> {
41 let seed = seed(challenge, nonce);
42 let solutions = equix::solve(&seed).map_err(|_| DrillxError::BadEquix)?;
43 if solutions.is_empty() {
44 return Err(DrillxError::NoSolutions);
45 }
46 let solution = unsafe { solutions.get_unchecked(0) };
48 Ok(solution.to_bytes())
49}
50
51#[inline(always)]
53fn digest_with_memory(
54 memory: &mut equix::SolverMemory,
55 challenge: &[u8; 32],
56 nonce: &[u8; 8],
57) -> Result<[u8; 16], DrillxError> {
58 let seed = seed(challenge, nonce);
59 let equix = equix::EquiXBuilder::new()
60 .runtime(equix::RuntimeOption::TryCompile)
61 .build(&seed)
62 .map_err(|_| DrillxError::BadEquix)?;
63 let solutions = equix.solve_with_memory(memory);
64 if solutions.is_empty() {
65 return Err(DrillxError::NoSolutions);
66 }
67 let solution = unsafe { solutions.get_unchecked(0) };
69 Ok(solution.to_bytes())
70}
71
72#[inline(always)]
74fn sorted(digest: [u8; 16]) -> [u8; 16] {
75 let mut sorted_digest = digest;
76 unsafe {
77 let u16_slice: &mut [u16; 8] = core::mem::transmute(&mut sorted_digest);
78 u16_slice.sort_unstable();
79 }
80 sorted_digest
81}
82
83#[cfg(feature = "solana")]
85#[inline(always)]
86fn hashv(digest: &[u8; 16], nonce: &[u8; 8]) -> [u8; 32] {
87 solana_program::keccak::hashv(&[sorted(*digest).as_slice(), nonce.as_slice()]).to_bytes()
88}
89
90#[cfg(not(feature = "solana"))]
92#[inline(always)]
93fn hashv(digest: &[u8; 16], nonce: &[u8; 8]) -> [u8; 32] {
94 let mut hasher = sha3::Keccak256::new();
95 hasher.update(&sorted(*digest));
96 hasher.update(nonce);
97 hasher.finalize().into()
98}
99
100pub fn is_valid_digest(challenge: &[u8; 32], nonce: &[u8; 8], digest: &[u8; 16]) -> bool {
102 let seed = seed(challenge, nonce);
103 equix::verify_bytes(&seed, digest).is_ok()
104}
105
106pub fn difficulty(hash: [u8; 32]) -> u32 {
108 let mut count = 0;
109 for &byte in &hash {
110 let lz = byte.leading_zeros();
111 count += lz;
112 if lz < 8 {
113 break;
114 }
115 }
116 count
117}
118
119#[derive(Default)]
121pub struct Hash {
122 pub d: [u8; 16], pub h: [u8; 32], }
125
126impl Hash {
127 pub fn difficulty(&self) -> u32 {
129 difficulty(self.h)
130 }
131}
132
133#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
135pub struct Solution {
136 pub d: [u8; 16], pub n: [u8; 8], }
139
140impl Solution {
141 pub fn new(digest: [u8; 16], nonce: [u8; 8]) -> Solution {
143 Solution { d: digest, n: nonce }
144 }
145
146 pub fn is_valid(&self, challenge: &[u8; 32]) -> bool {
148 is_valid_digest(challenge, &self.n, &self.d)
149 }
150
151 pub fn to_hash(&self) -> Hash {
153 Hash {
154 d: self.d,
155 h: hashv(&self.d, &self.n),
156 }
157 }
158}
159
160#[derive(Debug)]
161pub enum DrillxError {
162 BadEquix,
163 NoSolutions,
164}
165
166impl std::fmt::Display for DrillxError {
167 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
168 match *self {
169 DrillxError::BadEquix => write!(f, "Failed Equix"),
170 DrillxError::NoSolutions => write!(f, "No solutions"),
171 }
172 }
173}
174
175impl std::error::Error for DrillxError {
176 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
177 None
178 }
179}