1use std::convert::AsRef;
26
27use blake2::{digest::consts::U4, Blake2b, Digest};
28pub use equix::{EquiXBuilder, HashError, RuntimeOption, Solution, SolverMemory};
29
30use darkfi::{Error, Result};
31
32const P_STRING: &[u8] = b"DarkFi Equi-X\0";
34
35const P_STRING_LEN: usize = 14;
37
38pub const NONCE_LEN: usize = 16;
40
41#[derive(Debug, Clone, Eq, PartialEq)]
43pub struct Challenge(Vec<u8>);
44
45impl Challenge {
46 pub fn new(input: &[u8], nonce: &[u8; NONCE_LEN]) -> Self {
51 let mut result = Vec::<u8>::new();
52 result.extend_from_slice(P_STRING);
53 result.extend_from_slice(input.as_ref());
54 result.extend_from_slice(nonce.as_ref());
55
56 Self(result)
57 }
58
59 pub fn to_bytes(&self) -> Vec<u8> {
60 self.0.clone()
61 }
62
63 pub fn input(&self) -> Vec<u8> {
65 self.0[P_STRING_LEN..(self.0.len() - NONCE_LEN)].into()
66 }
67
68 pub fn nonce(&self) -> [u8; NONCE_LEN] {
70 self.0[(self.0.len() - NONCE_LEN)..].try_into().expect("slice length correct")
71 }
72
73 pub fn increment_nonce(&mut self) {
75 fn inc_le_bytes(slice: &mut [u8]) {
76 for byte in slice {
77 let (value, overflow) = (*byte).overflowing_add(1);
78 *byte = value;
79 if !overflow {
80 break;
81 }
82 }
83 }
84 let len = self.0.len();
85 inc_le_bytes(&mut self.0[(len - NONCE_LEN)..]);
86 }
87
88 pub fn check_effort(&self, proof: &equix::SolutionByteArray, effort: u32) -> bool {
90 let mut hasher = Blake2b::<U4>::new();
91 hasher.update(self.as_ref());
92 hasher.update(proof.as_ref());
93 let value = u32::from_be_bytes(hasher.finalize().into());
94 value.checked_mul(effort).is_some()
95 }
96}
97
98impl AsRef<[u8]> for Challenge {
99 fn as_ref(&self) -> &[u8] {
100 self.0.as_ref()
101 }
102}
103
104pub struct EquiXPow {
105 pub effort: u32,
107 pub challenge: Challenge,
109 pub equix: EquiXBuilder,
111 pub mem: SolverMemory,
113}
114
115impl EquiXPow {
116 pub fn run(&mut self) -> Result<Solution> {
117 loop {
118 if let Some(solution) = self.run_step()? {
119 return Ok(solution);
120 }
121 }
122 }
123
124 pub fn run_step(&mut self) -> Result<Option<Solution>> {
125 match self.equix.build(self.challenge.as_ref()) {
126 Ok(equix) => {
127 for candidate in equix.solve_with_memory(&mut self.mem) {
128 if self.challenge.check_effort(&candidate.to_bytes(), self.effort) {
129 return Ok(Some(candidate))
130 }
131 }
132 }
133 Err(equix::Error::Hash(HashError::ProgramConstraints)) => (),
134 Err(e) => {
135 return Err(Error::Custom(e.to_string()));
136 }
137 };
138 self.challenge.increment_nonce();
139 Ok(None)
140 }
141
142 pub fn verify(&self, challenge: &Challenge, solution: &Solution) -> Result<()> {
143 if challenge.check_effort(&solution.to_bytes(), self.effort) {
144 return self
145 .equix
146 .verify(challenge.as_ref(), solution)
147 .map_err(|e| Error::Custom(e.to_string()))
148 }
149 Err(Error::Custom("Equi-X solution has insufficient effort".into()))
150 }
151}