lib.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::convert::AsRef;
  19. use blake2::{digest::consts::U4, Blake2b, Digest};
  20. pub use equix::{EquiXBuilder, HashError, RuntimeOption, Solution, SolverMemory};
  21. /// Algorithm personalization string
  22. const P_STRING: &[u8] = b"DarkFi Equi-X\0";
  23. /// Length of the personalization string, in bytes
  24. const P_STRING_LEN: usize = 14;
  25. /// Length of the nonce value generated by clients and included in the solution
  26. pub const NONCE_LEN: usize = 16;
  27. /// A challenge string
  28. #[derive(Debug, Clone, Eq, PartialEq)]
  29. pub struct Challenge(pub Vec<u8>);
  30. impl Challenge {
  31. /// Build a new [`Challenge`].
  32. ///
  33. /// Copies `input` and `nonce` values into
  34. /// a new byte vector.
  35. pub fn new(input: &[u8], nonce: &[u8; NONCE_LEN]) -> Self {
  36. let mut result = Vec::<u8>::new();
  37. result.extend_from_slice(P_STRING);
  38. result.extend_from_slice(input.as_ref());
  39. result.extend_from_slice(nonce.as_ref());
  40. Self(result)
  41. }
  42. /// Clone the input portion of this challenge.
  43. pub fn input(&self) -> Vec<u8> {
  44. self.0[P_STRING_LEN..(self.0.len() - NONCE_LEN)].into()
  45. }
  46. /// Clone the nonce portion of this challenge.
  47. pub fn nonce(&self) -> [u8; NONCE_LEN] {
  48. self.0[(self.0.len() - NONCE_LEN)..].try_into().expect("slice length correct")
  49. }
  50. /// Increment the nonce value inside this challenge.
  51. pub fn increment_nonce(&mut self) {
  52. fn inc_le_bytes(slice: &mut [u8]) {
  53. for byte in slice {
  54. let (value, overflow) = (*byte).overflowing_add(1);
  55. *byte = value;
  56. if !overflow {
  57. break;
  58. }
  59. }
  60. }
  61. let len = self.0.len();
  62. inc_le_bytes(&mut self.0[(len - NONCE_LEN)..]);
  63. }
  64. // Verify that a solution proof passes the effort test.
  65. pub fn check_effort(&self, proof: &equix::SolutionByteArray, effort: u32) -> bool {
  66. let mut hasher = Blake2b::<U4>::new();
  67. hasher.update(self.as_ref());
  68. hasher.update(proof.as_ref());
  69. let value = u32::from_be_bytes(hasher.finalize().into());
  70. value.checked_mul(effort).is_some()
  71. }
  72. }
  73. impl AsRef<[u8]> for Challenge {
  74. fn as_ref(&self) -> &[u8] {
  75. self.0.as_ref()
  76. }
  77. }
  78. pub struct EquiXPow {
  79. /// Target effort
  80. pub effort: u32,
  81. /// The next [`Challenge`] to try
  82. pub challenge: Challenge,
  83. /// Configuration settings for Equi-X
  84. pub equix: EquiXBuilder,
  85. /// Temporary memory for Equi-X to use
  86. pub mem: SolverMemory,
  87. }
  88. impl EquiXPow {
  89. pub fn run(&mut self) -> Result<Solution, equix::Error> {
  90. loop {
  91. if let Some(solution) = self.run_step()? {
  92. return Ok(solution);
  93. }
  94. }
  95. }
  96. pub fn run_step(&mut self) -> Result<Option<Solution>, equix::Error> {
  97. match self.equix.build(self.challenge.as_ref()) {
  98. Ok(equix) => {
  99. for candidate in equix.solve_with_memory(&mut self.mem) {
  100. if self.challenge.check_effort(&candidate.to_bytes(), self.effort) {
  101. return Ok(Some(candidate))
  102. }
  103. }
  104. }
  105. Err(equix::Error::Hash(HashError::ProgramConstraints)) => (),
  106. Err(e) => {
  107. return Err(e);
  108. }
  109. };
  110. self.challenge.increment_nonce();
  111. Ok(None)
  112. }
  113. pub fn verify(&self, challenge: &Challenge, solution: &Solution) -> Result<(), equix::Error> {
  114. if challenge.check_effort(&solution.to_bytes(), self.effort) {
  115. return self.equix.verify(challenge.as_ref(), solution)
  116. }
  117. Err(equix::Error::HashSum)
  118. }
  119. }