mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-07 22:04:03 -05:00
script/research: add equix
This commit is contained in:
2
script/research/equix/.gitignore
vendored
Normal file
2
script/research/equix/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
21
script/research/equix/Cargo.toml
Normal file
21
script/research/equix/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "equix"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "equix_pow"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
blake2 = "0.10.6"
|
||||
criterion = { version = "0.7.0", features = ["html_reports"] }
|
||||
equix = "0.2.5"
|
||||
rand = "0.8.5"
|
||||
|
||||
[[bench]]
|
||||
name = "equix"
|
||||
harness = false
|
||||
path = "src/bench.rs"
|
||||
64
script/research/equix/src/bench.rs
Normal file
64
script/research/equix/src/bench.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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 criterion::{criterion_group, criterion_main, Criterion};
|
||||
use equix_pow::{Challenge, EquiXBuilder, EquiXPow, Solution, SolverMemory, NONCE_LEN};
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use std::hint::black_box;
|
||||
|
||||
fn new_challenge() -> Challenge {
|
||||
let mut rng = rand::thread_rng();
|
||||
let random: Vec<u8> = (0..32 + NONCE_LEN).map(|_| rng.gen()).collect();
|
||||
Challenge(random)
|
||||
}
|
||||
|
||||
fn new_equix() -> EquiXPow {
|
||||
EquiXPow {
|
||||
effort: 1000,
|
||||
challenge: new_challenge(),
|
||||
equix: EquiXBuilder::default(),
|
||||
mem: SolverMemory::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn benchmark_equix_pow(c: &mut Criterion) {
|
||||
let mut solutions: Vec<(Challenge, Solution)> = Vec::new();
|
||||
|
||||
let mut equix_pow = new_equix();
|
||||
c.bench_function(&format!("EquiXPow::run effort={}", equix_pow.effort), |b| {
|
||||
b.iter(|| {
|
||||
equix_pow.challenge = new_challenge();
|
||||
let solution = black_box(equix_pow.run().unwrap());
|
||||
solutions.push((equix_pow.challenge.clone(), solution));
|
||||
});
|
||||
});
|
||||
|
||||
let equix_pow = new_equix();
|
||||
c.bench_function(&format!("EquiXPow::verify effort={}", equix_pow.effort), |b| {
|
||||
b.iter(|| {
|
||||
let (challenge, solution) =
|
||||
black_box(solutions.choose(&mut rand::thread_rng()).unwrap());
|
||||
if let Err(e) = equix_pow.verify(challenge, solution) {
|
||||
eprintln!("Verification failed: {:?}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, benchmark_equix_pow);
|
||||
criterion_main!(benches);
|
||||
136
script/research/equix/src/lib.rs
Normal file
136
script/research/equix/src/lib.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
/* 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user