| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::convert::AsRef;
- use blake2::{digest::consts::U4, Blake2b, Digest};
- pub use equix::{EquiXBuilder, HashError, RuntimeOption, Solution, SolverMemory};
- /// Algorithm personalization string
- const P_STRING: &[u8] = b"DarkFi Equi-X\0";
- /// Length of the personalization string, in bytes
- const P_STRING_LEN: usize = 14;
- /// Length of the nonce value generated by clients and included in the solution
- pub const NONCE_LEN: usize = 16;
- /// A challenge string
- #[derive(Debug, Clone, Eq, PartialEq)]
- pub struct Challenge(pub Vec<u8>);
- impl Challenge {
- /// Build a new [`Challenge`].
- ///
- /// Copies `input` and `nonce` values into
- /// a new byte vector.
- pub fn new(input: &[u8], nonce: &[u8; NONCE_LEN]) -> Self {
- let mut result = Vec::<u8>::new();
- result.extend_from_slice(P_STRING);
- result.extend_from_slice(input.as_ref());
- result.extend_from_slice(nonce.as_ref());
- Self(result)
- }
- /// Clone the input portion of this challenge.
- pub fn input(&self) -> Vec<u8> {
- self.0[P_STRING_LEN..(self.0.len() - NONCE_LEN)].into()
- }
- /// Clone the nonce portion of this challenge.
- pub fn nonce(&self) -> [u8; NONCE_LEN] {
- self.0[(self.0.len() - NONCE_LEN)..].try_into().expect("slice length correct")
- }
- /// Increment the nonce value inside this challenge.
- pub fn increment_nonce(&mut self) {
- fn inc_le_bytes(slice: &mut [u8]) {
- for byte in slice {
- let (value, overflow) = (*byte).overflowing_add(1);
- *byte = value;
- if !overflow {
- break;
- }
- }
- }
- let len = self.0.len();
- inc_le_bytes(&mut self.0[(len - NONCE_LEN)..]);
- }
- // Verify that a solution proof passes the effort test.
- pub fn check_effort(&self, proof: &equix::SolutionByteArray, effort: u32) -> bool {
- let mut hasher = Blake2b::<U4>::new();
- hasher.update(self.as_ref());
- hasher.update(proof.as_ref());
- let value = u32::from_be_bytes(hasher.finalize().into());
- value.checked_mul(effort).is_some()
- }
- }
- impl AsRef<[u8]> for Challenge {
- fn as_ref(&self) -> &[u8] {
- self.0.as_ref()
- }
- }
- pub struct EquiXPow {
- /// Target effort
- pub effort: u32,
- /// The next [`Challenge`] to try
- pub challenge: Challenge,
- /// Configuration settings for Equi-X
- pub equix: EquiXBuilder,
- /// Temporary memory for Equi-X to use
- pub mem: SolverMemory,
- }
- impl EquiXPow {
- pub fn run(&mut self) -> Result<Solution, equix::Error> {
- loop {
- if let Some(solution) = self.run_step()? {
- return Ok(solution);
- }
- }
- }
- pub fn run_step(&mut self) -> Result<Option<Solution>, equix::Error> {
- match self.equix.build(self.challenge.as_ref()) {
- Ok(equix) => {
- for candidate in equix.solve_with_memory(&mut self.mem) {
- if self.challenge.check_effort(&candidate.to_bytes(), self.effort) {
- return Ok(Some(candidate))
- }
- }
- }
- Err(equix::Error::Hash(HashError::ProgramConstraints)) => (),
- Err(e) => {
- return Err(e);
- }
- };
- self.challenge.increment_nonce();
- Ok(None)
- }
- pub fn verify(&self, challenge: &Challenge, solution: &Solution) -> Result<(), equix::Error> {
- if challenge.check_effort(&solution.to_bytes(), self.effort) {
- return self.equix.verify(challenge.as_ref(), solution)
- }
- Err(equix::Error::HashSum)
- }
- }
|