mirror of
https://codeberg.org/wowario/wownero
synced 2026-10-21 06:18:40 +02:00
build cache with Argon2d
This commit is contained in:
+27
-34
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// Wownero Blake-AES-Groestl ("WBAG")
|
||||
// Wownero Blake-Argon2d-Groestl ("WBAG")
|
||||
//
|
||||
// Memory-hard hash function that combines BLAKE256 and Groestl256 using hardware-accelerated AES
|
||||
// - Cache: parameterized, power-of-two, default 256 MiB of 64-byte entries derived via BLAKE256
|
||||
// Memory-hard hash function that combines BLAKE256, Argon2d, and Groestl256 using hardware-accelerated AES
|
||||
// - Cache: Argon2d-derived buffer of size WBAG_CACHE_BYTES (default 256 MiB)
|
||||
// - Prehash: st = BLAKE256('m' || bd), parsed as four 64-bit little-endian lanes
|
||||
// - Rounds: data-dependent cache index, XOR entry words into lanes, rotate by SplitMix64-derived counts,
|
||||
// then one AES round per 128-bit pair (two total) using w0..w3 as the round keys
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
#include "wbag-hash.h"
|
||||
#include "blake256.h"
|
||||
#include "argon2/argon2.h"
|
||||
#include "aesb.h"
|
||||
#include "groestl.h"
|
||||
|
||||
@@ -69,6 +70,14 @@ int WBAG_get_aes_mode(void) { return g_wbag_aes_mode; }
|
||||
|
||||
#define WBAG_N_ENTRIES ( (WBAG_CACHE_BYTES / WBAG_ENTRY_BYTES) )
|
||||
|
||||
#ifndef WBAG_ARGON2_P
|
||||
#define WBAG_ARGON2_P 2u //threads
|
||||
#endif
|
||||
#ifndef WBAG_ARGON2_T
|
||||
#define WBAG_ARGON2_T 1u // passes
|
||||
#endif
|
||||
_Static_assert(WBAG_ARGON2_P == 2u, "WBAG requires Argon2 p=2");
|
||||
|
||||
// Upper bound to mitigate large-input in WBAG_hash() allocation
|
||||
#ifndef WBAG_MAX_BD_LEN
|
||||
#define WBAG_MAX_BD_LEN (16u * 1024u * 1024u) // 16 MiB
|
||||
@@ -186,13 +195,12 @@ static inline uint64_t WBAG_splitmix64(uint64_t x) {
|
||||
|
||||
// 32-bit FNV-1a over the textual description of params
|
||||
uint32_t WBAG_params_signature(void) {
|
||||
char buf[96];
|
||||
char buf[160];
|
||||
int n = snprintf(buf, sizeof(buf),
|
||||
"cache=%zu;entry=%zu;entries=%zu;rounds=%u",
|
||||
(size_t)WBAG_CACHE_BYTES,
|
||||
(size_t)WBAG_ENTRY_BYTES,
|
||||
(size_t)WBAG_N_ENTRIES,
|
||||
(unsigned)WBAG_ROUNDS);
|
||||
"cache=%zu;entry=%zu;entries=%zu;rounds=%u;cache_prf=argon2d;ver=0x%02x;t=%u;p=%u",
|
||||
(size_t)WBAG_CACHE_BYTES, (size_t)WBAG_ENTRY_BYTES, (size_t)WBAG_N_ENTRIES,
|
||||
(unsigned)WBAG_ROUNDS, (unsigned)ARGON2_VERSION_13,
|
||||
(unsigned)WBAG_ARGON2_T, (unsigned)WBAG_ARGON2_P);
|
||||
if (n < 0) n = 0;
|
||||
if (n >= (int)sizeof(buf)) n = (int)sizeof(buf) - 1;
|
||||
|
||||
@@ -286,35 +294,20 @@ void WBAG_epoch_seed(uint64_t epoch, const uint8_t anchor_hash[32], uint8_t out3
|
||||
}
|
||||
|
||||
// Cache builder
|
||||
void WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base, uint64_t start, uint64_t end)
|
||||
int WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base)
|
||||
{
|
||||
if (!seed32 || !out_base) return;
|
||||
if (start >= end) return;
|
||||
if (end > (uint64_t)WBAG_N_ENTRIES) return;
|
||||
if (!seed32 || !out_base) return -1;
|
||||
|
||||
enum { IN_LEN = 32 + 4 + 4 };
|
||||
uint8_t in[IN_LEN];
|
||||
memcpy(in, seed32, 32);
|
||||
const uint32_t m_cost_kib = (uint32_t)(WBAG_CACHE_BYTES / 1024u);
|
||||
const uint32_t p = WBAG_ARGON2_P;
|
||||
const uint32_t t_cost = (uint32_t)WBAG_ARGON2_T;
|
||||
|
||||
const size_t eb = (size_t)WBAG_ENTRY_BYTES;
|
||||
uint8_t *base = out_base + (size_t)start * eb;
|
||||
|
||||
for (uint64_t i = start; i < end; i++) {
|
||||
uint8_t *entry = base + (size_t)(i - start) * eb;
|
||||
WBAG_store32_le(&in[32], (uint32_t)i);
|
||||
|
||||
size_t written = 0;
|
||||
uint32_t blk = 0;
|
||||
uint8_t out[32];
|
||||
while (written < eb) {
|
||||
WBAG_store32_le(&in[36], blk++);
|
||||
blake256_hash(out, in, (uint64_t)IN_LEN);
|
||||
size_t take = eb - written;
|
||||
if (take > 32) take = 32;
|
||||
memcpy(entry + written, out, take);
|
||||
written += take;
|
||||
}
|
||||
int rc = argon2_hash(t_cost, m_cost_kib, p, seed32, 32, "WBAG-cache", 10, out_base, (size_t)WBAG_CACHE_BYTES, NULL, 0, Argon2_d, ARGON2_VERSION_13);
|
||||
if (rc != ARGON2_OK) {
|
||||
memset(out_base, 0, (size_t)WBAG_CACHE_BYTES);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Hash: prehash, rounds with AES mix, Groestl finalize
|
||||
|
||||
@@ -12,7 +12,8 @@ void WBAG_set_aes_mode(int mode);
|
||||
int WBAG_get_aes_mode(void);
|
||||
|
||||
void WBAG_epoch_seed(uint64_t epoch, const uint8_t anchor_hash[32], uint8_t out32[32]);
|
||||
void WBAG_build_cache(const uint8_t seed[32], uint8_t *out_base, uint64_t start, uint64_t end);
|
||||
|
||||
int WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base);
|
||||
|
||||
int WBAG_hash(const void *bd, size_t bd_len, const uint8_t *cache, uint8_t out32[32]);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "string_tools.h"
|
||||
@@ -47,15 +46,8 @@ inline bool cache_matches(uint64_t e, const crypto::hash &ah) {
|
||||
return p && p->epoch == e && p->anchor_hash == ah;
|
||||
}
|
||||
|
||||
// Build threads; 2 threads by default
|
||||
static std::atomic<unsigned> g_build_threads{2};
|
||||
|
||||
} // namespace
|
||||
|
||||
void set_build_threads(unsigned t) noexcept {
|
||||
g_build_threads.store(t, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool ensure_cache(uint64_t e, const crypto::hash &ah)
|
||||
{
|
||||
if (cache_matches(e, ah))
|
||||
@@ -109,36 +101,10 @@ bool ensure_cache(uint64_t e, const crypto::hash &ah)
|
||||
|
||||
alignas(64) uint8_t seed[32];
|
||||
epoch_seed_from_anchor(e, ah, seed);
|
||||
unsigned req = g_build_threads.load(std::memory_order_relaxed);
|
||||
unsigned hw = std::thread::hardware_concurrency();
|
||||
if (hw == 0) hw = 1;
|
||||
unsigned T = req ? req : hw;
|
||||
if (T < 1) T = 1;
|
||||
const uint64_t n_entries = n;
|
||||
if (T > n_entries) T = static_cast<unsigned>(n_entries);
|
||||
|
||||
std::atomic<bool> failed{false};
|
||||
if (T == 1) {
|
||||
WBAG_build_cache(seed, fresh->data.data(), 0, n_entries);
|
||||
} else {
|
||||
std::vector<std::thread> workers;
|
||||
workers.reserve(T);
|
||||
for (unsigned i = 0; i < T; ++i) {
|
||||
const uint64_t start = (static_cast<uint64_t>(i) * n_entries) / T;
|
||||
const uint64_t end = (static_cast<uint64_t>(i + 1) * n_entries) / T;
|
||||
workers.emplace_back([&, start, end]() noexcept {
|
||||
try {
|
||||
WBAG_build_cache(seed, fresh->data.data(), start, end);
|
||||
} catch (...) {
|
||||
failed.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &t : workers) { try { t.join(); } catch (...) { failed.store(true, std::memory_order_relaxed); } }
|
||||
if (failed.load(std::memory_order_relaxed)) {
|
||||
MERROR("WBAG parallel build failed; not publishing" << " (epoch=" << e << ", anchor=" << anchor_hex << ")");
|
||||
return false;
|
||||
}
|
||||
if (WBAG_build_cache(seed, fresh->data.data()) != 0) {
|
||||
MERROR("WBAG Argon2d cache build failed" << " (epoch=" << e << ", anchor=" << anchor_hex << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// final re-check to avoid duplicate publish
|
||||
|
||||
Reference in New Issue
Block a user