zebra_chain/work/
equihash.rs1use std::{fmt, io};
4
5use hex::{FromHex, FromHexError, ToHex};
6use serde_big_array::BigArray;
7
8use crate::{
9 block::Header,
10 parameters::Network,
11 serialization::{
12 zcash_deserialize_bytes_external_count, zcash_serialize_bytes, CompactSizeMessage,
13 SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
14 },
15};
16
17#[cfg(feature = "internal-miner")]
18use crate::serialization::AtLeastOne;
19
20#[non_exhaustive]
22#[derive(Debug, thiserror::Error)]
23#[error("invalid equihash solution for BlockHeader")]
24pub struct Error(#[from] equihash::Error);
25
26#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
28#[error("solver was cancelled")]
29pub struct SolverCancelled;
30
31pub(crate) const SOLUTION_SIZE: usize = 1344;
33
34pub(crate) const REGTEST_SOLUTION_SIZE: usize = 36;
36
37#[derive(Deserialize, Serialize)]
46#[allow(clippy::large_enum_variant)]
48pub enum Solution {
49 Common(#[serde(with = "BigArray")] [u8; SOLUTION_SIZE]),
51 Regtest(#[serde(with = "BigArray")] [u8; REGTEST_SOLUTION_SIZE]),
53}
54
55impl Solution {
56 pub const INPUT_LENGTH: usize = 4 + 32 * 3 + 4 * 2;
62
63 fn value(&self) -> &[u8] {
65 match self {
66 Solution::Common(solution) => solution.as_slice(),
67 Solution::Regtest(solution) => solution.as_slice(),
68 }
69 }
70
71 #[allow(clippy::unwrap_in_result)]
73 pub fn check(&self, header: &Header) -> Result<(), Error> {
74 let n = 200;
78 let k = 9;
79 let nonce = &header.nonce;
80
81 let mut input = Vec::new();
82 header
83 .zcash_serialize(&mut input)
84 .expect("serialization into a vec can't fail");
85
86 let input = &input[0..Solution::INPUT_LENGTH];
89
90 equihash::is_valid_solution(n, k, input, nonce.as_ref(), self.value())?;
91
92 Ok(())
93 }
94
95 pub fn from_bytes(solution: &[u8]) -> Result<Self, SerializationError> {
98 match solution.len() {
99 SOLUTION_SIZE => {
101 let mut bytes = [0; SOLUTION_SIZE];
102 bytes.copy_from_slice(solution);
103 Ok(Self::Common(bytes))
104 }
105 REGTEST_SOLUTION_SIZE => {
106 let mut bytes = [0; REGTEST_SOLUTION_SIZE];
107 bytes.copy_from_slice(solution);
108 Ok(Self::Regtest(bytes))
109 }
110 _unexpected_len => Err(SerializationError::Parse(
111 "incorrect equihash solution size",
112 )),
113 }
114 }
115
116 pub const SERIALIZED_SIZE: usize = 3 + SOLUTION_SIZE;
119
120 pub const REGTEST_SERIALIZED_SIZE: usize = 1 + REGTEST_SOLUTION_SIZE;
123
124 pub fn serialized_size(network: &Network) -> usize {
130 if network.is_regtest() {
131 Self::REGTEST_SERIALIZED_SIZE
132 } else {
133 Self::SERIALIZED_SIZE
134 }
135 }
136
137 pub fn for_proposal() -> Self {
139 Self::Common([0; SOLUTION_SIZE])
141 }
142
143 #[cfg(feature = "internal-miner")]
155 #[allow(clippy::unwrap_in_result)]
156 pub fn solve<F>(
157 mut header: Header,
158 mut cancel_fn: F,
159 ) -> Result<AtLeastOne<Header>, SolverCancelled>
160 where
161 F: FnMut() -> Result<(), SolverCancelled>,
162 {
163 use crate::shutdown::is_shutting_down;
164
165 let mut input = Vec::new();
166 header
167 .zcash_serialize(&mut input)
168 .expect("serialization into a vec can't fail");
169 let input = &input[0..Solution::INPUT_LENGTH];
172
173 while !is_shutting_down() {
174 cancel_fn()?;
176
177 let solutions = equihash::tromp::solve_200_9(input, || {
178 if cancel_fn().is_err() {
180 return None;
181 }
182
183 Self::next_nonce(&mut header.nonce);
185 Some(*header.nonce)
186 });
187
188 let mut valid_solutions = Vec::new();
189
190 for solution in &solutions {
191 header.solution = Self::from_bytes(solution)
192 .expect("unexpected invalid solution: incorrect length");
193
194 if let Err(error) = header.solution.check(&header) {
196 info!(?error, "found invalid solution for header");
197 continue;
198 }
199
200 if Self::difficulty_is_valid(&header) {
201 valid_solutions.push(header);
202 }
203 }
204
205 match valid_solutions.try_into() {
206 Ok(at_least_one_solution) => return Ok(at_least_one_solution),
207 Err(_is_empty_error) => debug!(
208 solutions = ?solutions.len(),
209 "found valid solutions which did not pass the validity or difficulty checks"
210 ),
211 }
212 }
213
214 Err(SolverCancelled)
215 }
216
217 #[cfg(feature = "internal-miner")]
223 fn difficulty_is_valid(header: &Header) -> bool {
224 let difficulty_threshold = header
226 .difficulty_threshold
227 .to_expanded()
228 .expect("unexpected invalid header template: invalid difficulty threshold");
229
230 let hash = header.hash();
232
233 hash <= difficulty_threshold
236 }
237
238 #[cfg(feature = "internal-miner")]
241 fn next_nonce(nonce: &mut [u8; 32]) {
242 let _ignore_overflow = crate::primitives::byte_array::increment_big_endian(&mut nonce[..]);
243 }
244}
245
246impl PartialEq<Solution> for Solution {
247 fn eq(&self, other: &Solution) -> bool {
248 self.value() == other.value()
249 }
250}
251
252impl fmt::Debug for Solution {
253 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
254 f.debug_tuple("EquihashSolution")
255 .field(&hex::encode(self.value()))
256 .finish()
257 }
258}
259
260impl Copy for Solution {}
263
264impl Clone for Solution {
265 fn clone(&self) -> Self {
266 *self
267 }
268}
269
270impl Eq for Solution {}
271
272#[cfg(any(test, feature = "proptest-impl"))]
273impl Default for Solution {
274 fn default() -> Self {
275 Self::Common([0; SOLUTION_SIZE])
276 }
277}
278
279impl ZcashSerialize for Solution {
280 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
281 zcash_serialize_bytes(&self.value().to_vec(), writer)
282 }
283}
284
285impl ZcashDeserialize for Solution {
286 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
287 let len: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
288 let len: usize = len.into();
289
290 if len > SOLUTION_SIZE {
294 return Err(SerializationError::Parse(
295 "incorrect equihash solution size",
296 ));
297 }
298
299 let solution = zcash_deserialize_bytes_external_count(len, &mut reader)?;
300 Self::from_bytes(&solution)
301 }
302}
303
304impl ToHex for &Solution {
305 fn encode_hex<T: FromIterator<char>>(&self) -> T {
306 self.value().encode_hex()
307 }
308
309 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
310 self.value().encode_hex_upper()
311 }
312}
313
314impl ToHex for Solution {
315 fn encode_hex<T: FromIterator<char>>(&self) -> T {
316 (&self).encode_hex()
317 }
318
319 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
320 (&self).encode_hex_upper()
321 }
322}
323
324impl FromHex for Solution {
325 type Error = FromHexError;
326
327 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
328 let bytes = Vec::from_hex(hex)?;
329 Solution::from_bytes(&bytes).map_err(|_| FromHexError::InvalidStringLength)
330 }
331}