upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/randomx_pow.cpp
diff options
context:
space:
mode:
authorbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
committerbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
commit5ce650382352c1503f40abf08129ada69402af55 (patch)
tree6a18198901f1e2c4933f1ac53c9365eec512bf63 /src/randomx_pow.cpp
parentf34113291da7e5e3dc7d2468d4f9c212c05c584b (diff)
public commit
Diffstat (limited to 'src/randomx_pow.cpp')
-rw-r--r--src/randomx_pow.cpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/randomx_pow.cpp b/src/randomx_pow.cpp
new file mode 100644
index 0000000..d617ba2
--- /dev/null
+++ b/src/randomx_pow.cpp
@@ -0,0 +1,166 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3//
4// RandomX PoW implementation. See randomx_pow.h.
5
6#include "headers.h"
7#include <randomx.h>
8#include <thread>
9#include <vector>
10#include <mutex>
11
12// Fixed key (seed) that determines the RandomX cache/dataset. Keeping it
13// constant avoids per-epoch "reseed" logic; for the CPU+RAM fairness goal
14// this is sufficient (RandomX already neutralizes GPU/ASIC by design).
15static const char* RANDOMX_KEY = "Bitflash/RandomX/v1/one-cpu-one-vote";
16
17static randomx_flags g_flags = RANDOMX_FLAG_DEFAULT;
18static randomx_cache* g_cache = NULL;
19static randomx_dataset* g_dataset = NULL;
20static randomx_vm* g_vmVerify = NULL;
21static CCriticalSection g_csVerify;
22static bool g_fInit = false;
23static bool g_fFast = false;
24
25
26bool RandomXInit()
27{
28 if (g_fInit)
29 return true;
30
31 // Detect the best flags for this CPU (JIT, hardware AES, Argon2).
32 g_flags = randomx_get_flags();
33
34 g_cache = randomx_alloc_cache(g_flags);
35 if (!g_cache)
36 {
37 // Try without JIT as a fallback
38 g_flags = RANDOMX_FLAG_DEFAULT;
39 g_cache = randomx_alloc_cache(g_flags);
40 if (!g_cache)
41 return error("RandomX: failed to allocate cache (256 MB)");
42 }
43 randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY));
44
45 // Verification VM in light mode (cache only)
46 g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL);
47 if (!g_vmVerify)
48 return error("RandomX: failed to create verification VM");
49
50 g_fInit = true;
51 printf("RandomX: initialized (cache 256 MB, flags=%d)\n", (int)g_flags);
52 return true;
53}
54
55
56// One dataset, once, no matter how many miners ask for it.
57//
58// Every miner thread calls this at startup. The g_fFast guard below is only
59// set at the very end, after the ~2 GB dataset has been filled, which takes
60// tens of seconds -- so without this lock all of them sail past the guard
61// together and each allocates its own dataset. Measured in production: a
62// 32-core machine logged "initializing ~2 GB dataset" 34 times and committed
63// 65 GB of private pages against a startup message promising 2142 MB.
64//
65// The waste was the mild half. g_dataset is assigned the moment the memory is
66// allocated, before it holds anything, so the last thread to allocate would
67// swing the pointer out from under the threads still filling their own. A
68// miner could then hash against a dataset another thread had not finished
69// writing, producing proof of work that no other node can reproduce.
70//
71// Holding the lock across the whole initialisation is deliberate: a thread
72// that arrives mid-init has nothing useful to do until the dataset exists,
73// and blocking is how it waits for exactly that.
74static std::mutex g_csDataset;
75
76bool RandomXInitDataset(int nThreads)
77{
78 if (!g_fInit && !RandomXInit())
79 return false;
80
81 std::lock_guard<std::mutex> lock(g_csDataset);
82 if (g_fFast)
83 return true;
84
85 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM);
86 g_dataset = randomx_alloc_dataset(fastFlags);
87 if (!g_dataset)
88 {
89 printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n");
90 return false;
91 }
92
93 unsigned long total = randomx_dataset_item_count();
94 if (nThreads < 1) nThreads = 1;
95 printf("RandomX: initializing ~2 GB dataset with %d threads...\n", nThreads);
96
97 std::vector<std::thread> workers;
98 unsigned long per = total / nThreads;
99 for (int i = 0; i < nThreads; i++)
100 {
101 unsigned long start = i * per;
102 unsigned long count = (i == nThreads - 1) ? (total - start) : per;
103 workers.emplace_back([start, count]() {
104 randomx_init_dataset(g_dataset, g_cache, start, count);
105 });
106 }
107 for (auto& w : workers) w.join();
108
109 g_fFast = true;
110 printf("RandomX: dataset ready (fast mode active)\n");
111 return true;
112}
113
114
115bool RandomXFastReady()
116{
117 return g_fFast;
118}
119
120
121uint256 RandomXPoWHash(const void* pHeader, size_t nSize)
122{
123 uint256 result = 0;
124 if (!g_fInit && !RandomXInit())
125 return result;
126 CRITICAL_BLOCK(g_csVerify)
127 {
128 unsigned char hash[RANDOMX_HASH_SIZE];
129 randomx_calculate_hash(g_vmVerify, pHeader, nSize, hash);
130 memcpy(&result, hash, RANDOMX_HASH_SIZE);
131 }
132 return result;
133}
134
135
136void* RandomXCreateMinerVM()
137{
138 if (!g_fInit && !RandomXInit())
139 return NULL;
140 if (g_fFast && g_dataset)
141 {
142 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM);
143 return randomx_create_vm(fastFlags, NULL, g_dataset);
144 }
145 // Light mode: own VM sharing the cache
146 return randomx_create_vm(g_flags, g_cache, NULL);
147}
148
149
150void RandomXDestroyMinerVM(void* vm)
151{
152 if (vm)
153 randomx_destroy_vm((randomx_vm*)vm);
154}
155
156
157uint256 RandomXHashWithVM(void* vm, const void* pHeader, size_t nSize)
158{
159 uint256 result = 0;
160 if (!vm)
161 return result;
162 unsigned char hash[RANDOMX_HASH_SIZE];
163 randomx_calculate_hash((randomx_vm*)vm, pHeader, nSize, hash);
164 memcpy(&result, hash, RANDOMX_HASH_SIZE);
165 return result;
166}