==> src/sha256d.c <==1/* sha256d: Bitcoin-style double SHA-256 nonce grinding.
2 *
3 * A job program (plan F5): every chunk of 1 << chunk_log2 nonces is an item of
4 * a fleet job, leased by the leader and run as OP_JOB. The threads hash the
5 * chunk in parallel, ILP hashes per thread per iteration. The first 64-byte
6 * block of the header is constant, so its compression (the midstate) is
7 * computed once per header change and kept in the view.
8 *
9 * Nonce space: chunk g covers position p = g << chunk_log2: nonces base +
10 * (p mod 2^32) .. + chunk, with the header time rolled by p >> 32 (ntime =
11 * time + roll), so the search never repeats. chunk_log2 is the manifest's
12 * `param` (PX_PARAM), so every member of a cluster grinds the same chunk; a
13 * param outside CHUNK_LOG2_MIN..=CHUNK_LOG2_MAX fails every OP_JOB. When it
14 * changes (an upgrade) the next task restarts the search with the new size.
15 *
16 * Hash order: compared as a little-endian 256-bit number (Bitcoin): its most
17 * significant 32-bit word is bswap(H[7]) of the second SHA-256's state. The
18 * view stores hashes and the target as 32 bytes in that order.
19 *
20 * Hot loop: each thread keeps only its best (hash top 64 bits, nonce) and a
21 * candidate flag (top word <= the target's). If any thread saw a candidate,
22 * warp 0 rescans the chunk with the full compare and records every share, so
23 * the share count is exact and the hot loop has no data-dependent stores.
24 *
25 * Consistency: configuration is written by lane 0 of whichever node runs the
26 * op, under the view's spin lock, with gen odd meanwhile. A task reads the
27 * configuration under the gen seqlock and records its results under the lock
28 * only if gen is unchanged; a chunk that straddled a change is stale.
29 *
30 * Ops: OP_JOB grind chunk V_JOB_BASE + arg (its share count, or R_JOB_FAIL);
31 * OP_REQ (text or raw, below); 3 target = arg leading zero bits (1..=255); 4
32 * reset the stats; 5 target from compact nbits = arg; 6 the genesis header,
33 * nonce base 0, target arg zero bits (0: the header's own nbits); 7 refused
34 * (the chunk is the manifest's param); 8 nonce base = arg; 9 header time = arg.
35 * Request window: whitespace-separated `header <160 hex>`, `target <64 hex,
36 * display order>`, `zbits <n>`, `nbits <8 hex>`, `base <n>`, `time <n>`,
37 * `genesis`, `reset`, `status`; or raw bytes: 80 (header) or 112 (header,
38 * then the target in internal order), when the first byte is not a lowercase
39 * letter. The response is one text line (`ok ...` or `err ...`).
40 * Ported from alpha-rust-mesh programs/sha256d/src/lib.rs. */
41#include "mesh/prog/sha256d.h"
42#include "mesh/rt.h"
43
44/* The Bitcoin genesis block header, as 20 little-endian words. */
45static const uint32_t SHA_GENESIS[20] = {
46 0x00000001u, 0, 0, 0, 0, 0, 0, 0, 0, 0xfdeda33bu, 0xb2127b7au, 0x3e2cc77au, 0x618f7667u, 0xc31bc87fu, 0x32518a88u,
47 0xaab89f3au, 0x4a5e1e4bu, 0x495fab29u, 0x1d00ffffu, 0x7c2bac1du,
48};
49
50static const uint32_t SHA_K[64] = {
51 0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u, 0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
52 0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u, 0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
53 0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu, 0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
54 0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u, 0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
55 0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u, 0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
56 0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u, 0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
57 0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u, 0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
58 0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u, 0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u,
59};
60static const uint32_t SHA_IV[8] = {0x6a09e667u, 0xbb67ae85u, 0x3c6ef372u, 0xa54ff53au, 0x510e527fu, 0x9b05688cu, 0x1f83d9abu, 0x5be0cd19u};
61
62#define SHA_INLINE static inline __attribute__((always_inline))
63
64/* A rotate (a funnel shift, shf.r.wrap.b32, on the GPU). */
65SHA_INLINE uint32_t sha_rotr(uint32_t x, uint32_t n) { return (x >> n) | (x << ((32u - n) & 31u)); }
66SHA_INLINE uint32_t sha_bsig0(uint32_t a) { return sha_rotr(a, 2) ^ sha_rotr(a, 13) ^ sha_rotr(a, 22); }
67SHA_INLINE uint32_t sha_bsig1(uint32_t e) { return sha_rotr(e, 6) ^ sha_rotr(e, 11) ^ sha_rotr(e, 25); }
68SHA_INLINE uint32_t sha_ssig0(uint32_t x) { return sha_rotr(x, 7) ^ sha_rotr(x, 18) ^ (x >> 3); }
69SHA_INLINE uint32_t sha_ssig1(uint32_t x) { return sha_rotr(x, 17) ^ sha_rotr(x, 19) ^ (x >> 10); }
70SHA_INLINE uint32_t sha_ch(uint32_t e, uint32_t f, uint32_t g) { return g ^ (e & (f ^ g)); }
71SHA_INLINE uint32_t sha_maj(uint32_t a, uint32_t b, uint32_t c) { return (a & b) | (c & (a | b)); }
72/* Byte swap (prmt on the GPU). */
73SHA_INLINE uint32_t sha_bswap(uint32_t x) { return __builtin_bswap32(x); }
74
75/* Round i of N independent messages: r = i mod 16 (the schedule window's
76 * slot), kc = K[i]. N is ILP (2) or 1; every call is inlined with constants,
77 * so the window's slots are registers. */
78SHA_INLINE void sha_round(uint32_t s[8][SHA_ILP], uint32_t w[16][SHA_ILP], uint32_t n, uint32_t r, uint32_t kc, bool expand) {
79 for (uint32_t j = 0; j < n; j++) {
80 if (expand) {
81 uint32_t x = w[(r + 14) & 15][j], y = w[(r + 1) & 15][j];
82 w[r][j] = w[r][j] + sha_ssig1(x) + w[(r + 9) & 15][j] + sha_ssig0(y);
83 }
84 uint32_t a = s[0][j], b = s[1][j], c = s[2][j], d = s[3][j], e = s[4][j], f = s[5][j], g = s[6][j], h = s[7][j];
85 uint32_t t1 = h + sha_bsig1(e) + sha_ch(e, f, g) + kc + w[r][j];
86 uint32_t t2 = sha_bsig0(a) + sha_maj(a, b, c);
87 s[7][j] = g;
88 s[6][j] = f;
89 s[5][j] = e;
90 s[4][j] = d + t1;
91 s[3][j] = c;
92 s[2][j] = b;
93 s[1][j] = a;
94 s[0][j] = t1 + t2;
95 }
96}
97
98/* The 64 rounds. Rounds 0..16 (message words, often constant) and 48..64
99 * (whose tail the hot loop does not use) are unrolled so constants fold and
100 * dead rounds drop; rounds 16..48 are a 2-trip loop over 16 unrolled rounds,
101 * which keeps the hot loop's code small enough for the instruction cache. */
102SHA_INLINE void sha_rounds64(uint32_t s[8][SHA_ILP], uint32_t w[16][SHA_ILP], uint32_t n) {
103 for (uint32_t r = 0; r < 16; r++) sha_round(s, w, n, r, SHA_K[r], false);
104#if defined(__clang__)
105#pragma clang loop unroll(disable)
106#endif
107 for (uint32_t b = 16; b < 48; b += 16) {
108#if defined(__clang__)
109#pragma clang loop unroll(full)
110#endif
111 for (uint32_t r = 0; r < 16; r++) sha_round(s, w, n, r, SHA_K[b + r], true);
112 }
113 for (uint32_t r = 0; r < 16; r++) sha_round(s, w, n, r, SHA_K[48 + r], true);
114}
115
116/* SHA256d of n headers that differ only in the nonce: mid is the first
117 * block's state, w0..w2 the second block's first words (big-endian), nonce_be
118 * the byte-swapped nonces. Leaves the second SHA-256's state words H[0..8]
119 * (the hash bytes are their big-endian serialization) in out. */
120SHA_INLINE void sha_sha256d(const uint32_t mid[8], uint32_t w0, uint32_t w1, uint32_t w2, const uint32_t nonce_be[SHA_ILP],
121 uint32_t n, uint32_t out[8][SHA_ILP]) {
122 uint32_t s[8][SHA_ILP], w[16][SHA_ILP];
123 for (uint32_t j = 0; j < n; j++) {
124 for (uint32_t k = 0; k < 8; k++) s[k][j] = mid[k];
125 for (uint32_t k = 0; k < 16; k++) w[k][j] = 0;
126 w[0][j] = w0, w[1][j] = w1, w[2][j] = w2, w[3][j] = nonce_be[j], w[4][j] = 0x80000000u, w[15][j] = 640;
127 }
128 sha_rounds64(s, w, n);
129 for (uint32_t j = 0; j < n; j++) {
130 for (uint32_t k = 0; k < 16; k++) w[k][j] = 0;
131 for (uint32_t k = 0; k < 8; k++) w[k][j] = s[k][j] + mid[k];
132 w[8][j] = 0x80000000u, w[15][j] = 256;
133 for (uint32_t k = 0; k < 8; k++) s[k][j] = SHA_IV[k];
134 }
135 sha_rounds64(s, w, n);
136 for (uint32_t j = 0; j < n; j++)
137 for (uint32_t k = 0; k < 8; k++) out[k][j] = s[k][j] + SHA_IV[k];
138}
139
140/* One SHA256d as a little-endian 256-bit number (words, 7 most significant).
141 * Cold paths only (the best hash, the share rescan): one shared copy. */
142__attribute__((noinline)) static void sha_hash1(const uint32_t mid[8], uint32_t w0, uint32_t w1, uint32_t w2, uint32_t nonce, uint32_t out[8]) {
143 uint32_t nb[SHA_ILP] = {sha_bswap(nonce), 0}, h[8][SHA_ILP];
144 sha_sha256d(mid, w0, w1, w2, nb, 1, h);
145 for (uint32_t k = 0; k < 8; k++) out[k] = sha_bswap(h[k][0]);
146}
147
148/* ---- the view's spin lock and configuration (lane 0) */
149static void sha_lock(mesh_view v) {
150 uint8_t *p = mesh_view_ptr(v) + SHA_V_LOCK;
151 while (mesh_rt_atom_cas(p, 0, 1) != 0) mesh_rt_nanosleep(64);
152 mesh_rt_fence_acq_rel();
153}
154static void sha_unlock(mesh_view v) {
155 mesh_rt_fence_acq_rel();
156 mesh_rt_st_release(mesh_view_ptr(v) + SHA_V_LOCK, 0);
157}
158/* A configuration change begins (locked, gen odd); returns the odd gen for sha_config_end. */
159static uint32_t sha_config_begin(mesh_view v) {
160 sha_lock(v);
161 uint8_t *gp = mesh_view_ptr(v) + SHA_V_GEN;
162 uint32_t g = mesh_rt_ld_relaxed(gp) | 1u;
163 mesh_rt_st_relaxed(gp, g);
164 mesh_rt_fence_acq_rel();
165 return g;
166}
167static void sha_config_end(mesh_view v, uint32_t g) {
168 mesh_rt_fence_acq_rel();
169 mesh_rt_st_release(mesh_view_ptr(v) + SHA_V_GEN, g + 1);
170 sha_unlock(v);
171}
172
173/* ---- 256-bit helpers (words k = 7 most significant) */
174static uint32_t sha_le_word(uint32_t h) { return sha_bswap(h); }
175/* Leading zero bits of a little-endian 256-bit number given as words. */
176static uint32_t sha_zbits_of(const uint32_t w[8]) {
177 uint32_t z = 0;
178 bool done = false;
179 for (int k = 7; k >= 0; k--)
180 if (!done) {
181 if (w[k] == 0) z += 32;
182 else { z += (uint32_t)__builtin_clz(w[k]); done = true; }
183 }
184 return z;
185}
186static void sha_read8(mesh_view v, uint32_t off, uint32_t w[8]) { for (uint32_t k = 0; k < 8; k++) w[k] = mesh_view_u32(v, off + 4 * k); }
187static void sha_write8(mesh_view v, uint32_t off, const uint32_t w[8]) { for (uint32_t k = 0; k < 8; k++) mesh_view_set_u32(v, off + 4 * k, w[k]); }
188/* Compares little-endian 256-bit numbers: -1, 0, 1 (the most significant differing word decides). */
189static int sha_cmp256(const uint32_t a[8], const uint32_t b[8]) {
190 int r = 0;
191 for (int k = 0; k < 8; k++)
192 if (a[k] != b[k]) r = a[k] < b[k] ? -1 : 1;
193 return r;
194}
195
196/* The midstate of the header's first 64 bytes (cold: one shared copy). */
197__attribute__((noinline)) static void sha_set_midstate(mesh_view v) {
198 uint32_t s[8][SHA_ILP], w[16][SHA_ILP];
199 for (uint32_t k = 0; k < 8; k++) s[k][0] = SHA_IV[k];
200 /* Unchecked reads: init checked view_len >= VIEW_MIN (fixed per image). */
201 uint8_t *hp = mesh_view_ptr(v) + SHA_V_HEADER;
202 for (uint32_t k = 0; k < 16; k++) w[k][0] = sha_bswap(mesh_rt_rd(hp + 4 * k));
203 sha_rounds64(s, w, 1);
204 for (uint32_t k = 0; k < 8; k++) mesh_view_set_u32(v, SHA_V_MID + 4 * k, s[k][0] + SHA_IV[k]);
205}
206
207static void sha_clear_shares(mesh_view v) {
208 mesh_view_set_u32(v, SHA_V_SHARES, 0);
209 mesh_view_set_u32(v, SHA_V_FOUND, 0);
210 mesh_view_set_u32(v, SHA_V_LAST_NONCE, 0);
211 mesh_view_set_u32(v, SHA_V_LAST_TIME, 0);
212 mesh_view_set_u32(v, SHA_V_LAST_ZBITS, 0);
213 for (uint32_t o = SHA_V_RING; o < SHA_V_RING + SHA_RING * SHA_RING_BYTES; o += 4) mesh_view_set_u32(v, o, 0);
214}
215static void sha_clear_best(mesh_view v) {
216 const uint32_t all[8] = {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
217 sha_write8(v, SHA_V_BEST, all);
218 mesh_view_set_u32(v, SHA_V_BEST_ZBITS, 0);
219 mesh_view_set_u32(v, SHA_V_BEST_NONCE, 0);
220 mesh_view_set_u32(v, SHA_V_BEST_TIME, 0);
221 mesh_view_set_u32(v, SHA_V_BEST_HI, UINT32_MAX);
222}
223/* A new search over the same header (base, chunk, time or header changed). */
224static void sha_restart(mesh_view v) {
225 sha_clear_best(v);
226 sha_clear_shares(v);
227}
228/* The header at V_HEADER changed: its copies, the midstate, a new search. */
229static void sha_header_changed(mesh_view v) {
230 mesh_view_set_u32(v, SHA_V_VERSION, mesh_view_u32(v, SHA_V_HEADER));
231 mesh_view_set_u32(v, SHA_V_TIME, mesh_view_u32(v, SHA_V_HEADER + 68));
232 mesh_view_set_u32(v, SHA_V_NBITS, mesh_view_u32(v, SHA_V_HEADER + 72));
233 mesh_view_set_u32(v, SHA_V_HEADERS, mesh_view_u32(v, SHA_V_HEADERS) + 1);
234 sha_set_midstate(v);
235 sha_restart(v);
236}
237static void sha_target_changed(mesh_view v) {
238 uint32_t t[8];
239 sha_read8(v, SHA_V_TARGET, t);
240 mesh_view_set_u32(v, SHA_V_TARGET_ZBITS, sha_zbits_of(t));
241 sha_clear_shares(v);
242}
243/* Target = 2^(256 - z) - 1 (z leading zero bits). */
244static void sha_set_zbits(mesh_view v, uint32_t z) {
245 uint32_t lim = 256 - z;
246 for (uint32_t k = 0; k < 8; k++) {
247 uint32_t lo = 32 * k;
248 uint32_t w = lo + 32 <= lim ? UINT32_MAX : lo >= lim ? 0 : (1u << (lim - lo)) - 1;
249 mesh_view_set_u32(v, SHA_V_TARGET + 4 * k, w);
250 }
251 sha_target_changed(v);
252}
253/* Target from compact bits (mantissa * 256^(exp - 3)); false if negative or overflowing. */
254static bool sha_set_nbits(mesh_view v, uint32_t bits) {
255 uint32_t exp = bits >> 24, mant = bits & 0x007fffffu;
256 if ((bits & 0x00800000u) || exp > 32) return false;
257 int32_t pos = (int32_t)exp - 3;
258 if (pos < 0) { mant >>= 8 * (uint32_t)(-pos); pos = 0; }
259 for (uint32_t k = 0; k < 8; k++) mesh_view_set_u32(v, SHA_V_TARGET + 4 * k, 0);
260 for (uint32_t i = 0; i < 3; i++) {
261 uint32_t b = (mant >> (8 * i)) & 0xff, at = (uint32_t)pos + i;
262 if (at < 32) mesh_view_set_u8(v, SHA_V_TARGET + at, (uint8_t)b);
263 else if (b != 0) return false;
264 }
265 sha_target_changed(v);
266 return true;
267}
268static void sha_set_genesis(mesh_view v) {
269 for (uint32_t k = 0; k < 20; k++) mesh_view_set_u32(v, SHA_V_HEADER + 4 * k, SHA_GENESIS[k]);
270 mesh_view_set_u32(v, SHA_V_BASE, 0);
271 sha_header_changed(v);
272}
273static void sha_reset_stats(mesh_view v) {
274 mesh_view_set_u64(v, SHA_V_HASHES, 0);
275 sha_clear_best(v);
276 sha_clear_shares(v);
277}
278
279static void sha_init(const mesh_ctx *ctx) {
280 if (!mesh_ctx_leader(ctx)) return;
281 mesh_view v = mesh_ctx_view(ctx);
282 /* A view smaller than the layout faults here (bounds-checked), loudly. */
283 if (mesh_view_len(v) < SHA_VIEW_MIN) { (void)mesh_view_u32(v, SHA_VIEW_MIN - 4); return; }
284 uint32_t g = sha_config_begin(v);
285 mesh_view_set_u32(v, SHA_V_MAGIC, SHA_MAGIC);
286 mesh_view_set_u32(v, SHA_V_CHUNK_LOG2, mesh_ctx_param(ctx));
287 mesh_view_set_u32(v, SHA_V_SYSTEM, mesh_ctx_system(ctx));
288 sha_set_genesis(v);
289 sha_set_zbits(v, SHA_ZBITS_DEFAULT);
290 sha_reset_stats(v);
291 sha_config_end(v, g);
292}
293
294/* The chunk size the manifest gives (false: out of range). */
295static bool sha_chunk_log2(const mesh_ctx *ctx, uint32_t *c) {
296 *c = mesh_ctx_param(ctx);
297 return *c >= SHA_CHUNK_LOG2_MIN && *c <= SHA_CHUNK_LOG2_MAX;
298}
299/* Lane 0: if the param changed since the search began (an upgrade), restart
300 * the search with the new size (once: rechecked under the lock). */
301static void sha_sync_chunk(mesh_view v, uint32_t clog) {
302 if (mesh_view_u32(v, SHA_V_CHUNK_LOG2) != clog) {
303 uint32_t g = sha_config_begin(v);
304 if (mesh_view_u32(v, SHA_V_CHUNK_LOG2) != clog) {
305 mesh_view_set_u32(v, SHA_V_CHUNK_LOG2, clog);
306 sha_restart(v);
307 }
308 sha_config_end(v, g);
309 }
310}
311
312/* One share into the ring (lane 0), if the configuration is still g. */
313__attribute__((noinline)) static void sha_record_share(const mesh_ctx *ctx, mesh_view v, uint32_t g, uint32_t nonce, uint32_t ntime, const uint32_t h[8]) {
314 sha_lock(v);
315 if (mesh_rt_ld_relaxed(mesh_view_ptr(v) + SHA_V_GEN) == g) {
316 uint32_t s = mesh_view_u32(v, SHA_V_SHARES);
317 uint32_t e = SHA_V_RING + (s % SHA_RING) * SHA_RING_BYTES;
318 uint32_t z = sha_zbits_of(h);
319 mesh_view_set_u32(v, e, nonce);
320 mesh_view_set_u32(v, e + 4, ntime);
321 mesh_view_set_u32(v, e + 8, s + 1);
322 mesh_view_set_u32(v, e + 12, z);
323 sha_write8(v, e + 16, h);
324 mesh_view_set_u32(v, SHA_V_SHARES, s + 1);
325 mesh_view_set_u32(v, SHA_V_LAST_NONCE, nonce);
326 mesh_view_set_u32(v, SHA_V_LAST_TIME, ntime);
327 mesh_view_set_u32(v, SHA_V_LAST_ZBITS, z);
328 mesh_view_set_u32(v, SHA_V_FOUND, 1);
329 mesh_ctx_count(ctx, 1, 1);
330 }
331 sha_unlock(v);
332}
333
334/* The node's shared memory: the configuration warp 0 read (CFG_WORDS words). */
335#define SHA_CFG_WORDS 21u
336
337/* One leased chunk (OP_JOB: chunk V_JOB_BASE + job): hash, record. Every thread
338 * of the node: warp 0 reads the configuration into shared memory, the node
339 * grinds the chunk (nonce thread + threads x i), the best hash and the
340 * candidate flag by block reductions, then warp 0 alone records (and rescans
341 * the chunk when a thread saw a candidate: rare). */
342static uint32_t sha_grind(const mesh_ctx *ctx, uint32_t job) {
343 mesh_view v = mesh_ctx_view(ctx);
344 uint32_t lane = mesh_ctx_lane(ctx);
345 uint8_t *gp = mesh_view_ptr(v) + SHA_V_GEN;
346 uint32_t clog;
347 if (!sha_chunk_log2(ctx, &clog)) return MESH_R_JOB_FAIL;
348 mesh_shared cfg;
349 if (!mesh_ctx_shared(ctx, 0, SHA_CFG_WORDS, 4, &cfg)) return MESH_R_JOB_FAIL;
350 /* Threads at work: all, or (a chunk smaller than threads x ILP) the first chunk / ILP. */
351 uint32_t threads = mesh_ctx_threads(ctx) < (1u << clog) / SHA_ILP ? mesh_ctx_threads(ctx) : (1u << clog) / SHA_ILP;
352 if (mesh_ctx_warp(ctx) == 0) {
353 if (lane == 0) sha_sync_chunk(v, clog);
354 mesh_rt_warp_sync();
355 /* Configuration under the gen seqlock. */
356 uint32_t g, mid[8], hw[3], base, tgt[8];
357 for (;;) {
358 uint32_t g1 = 0;
359 if (lane == 0) {
360 g1 = mesh_rt_ld_acquire(gp);
361 while (g1 & 1u) { mesh_rt_nanosleep(256); g1 = mesh_rt_ld_acquire(gp); }
362 }
363 g1 = mesh_rt_shfl_idx(g1, 0);
364 mesh_rt_warp_sync();
365 sha_read8(v, SHA_V_MID, mid);
366 hw[0] = mesh_view_u32(v, SHA_V_HEADER + 64), hw[1] = mesh_view_u32(v, SHA_V_HEADER + 68), hw[2] = mesh_view_u32(v, SHA_V_HEADER + 72);
367 base = mesh_view_u32(v, SHA_V_BASE);
368 sha_read8(v, SHA_V_TARGET, tgt);
369 mesh_rt_fence_acq_rel();
370 mesh_rt_warp_sync();
371 uint32_t g2 = lane == 0 ? mesh_rt_ld_acquire(gp) : 0;
372 if (mesh_rt_shfl_idx(g2, 0) == g1) { g = g1; break; }
373 /* Read across a change: again under the new gen. */
374 }
375 if (lane == 0) {
376 const uint32_t head[5] = {g, base, hw[0], hw[1], hw[2]};
377 for (uint32_t i = 0; i < 5; i++) mesh_shared_set_u32(cfg, i, head[i]);
378 for (uint32_t i = 0; i < 8; i++) mesh_shared_set_u32(cfg, 5 + i, mid[i]);
379 for (uint32_t i = 0; i < 8; i++) mesh_shared_set_u32(cfg, 13 + i, tgt[i]);
380 }
381 }
382 mesh_ctx_block_barrier(ctx);
383 uint32_t g = mesh_shared_get_u32(cfg, 0), base = mesh_shared_get_u32(cfg, 1);
384 uint32_t hw[3] = {mesh_shared_get_u32(cfg, 2), mesh_shared_get_u32(cfg, 3), mesh_shared_get_u32(cfg, 4)};
385 uint32_t mid[8], tgt[8];
386 for (uint32_t i = 0; i < 8; i++) mid[i] = mesh_shared_get_u32(cfg, 5 + i), tgt[i] = mesh_shared_get_u32(cfg, 13 + i);
387 uint64_t gidx = mesh_view_u64(v, SHA_V_JOB_BASE) + job;
388 /* This item's result record: lane 0 fills it. */
389 uint32_t rec = SHA_V_JOB_OUT + SHA_JOB_REC * (mesh_ctx_ticket(ctx) % MESH_PROG_RESULTS);
390 uint32_t found = 0;
391 uint64_t p = gidx << clog;
392 uint32_t nonce0 = base + (uint32_t)p;
393 uint32_t ntime = hw[1] + (uint32_t)(p >> 32);
394 uint32_t w0 = sha_bswap(hw[0]), w1 = sha_bswap(ntime), w2 = sha_bswap(hw[2]);
395 uint32_t t7 = tgt[7];
396
397 /* The hot loop: registers only. Thread t takes nonces t + threads x i. */
398 uint32_t th = mesh_ctx_thread(ctx);
399 uint32_t iters = th < threads ? (1u << clog) / (threads * SHA_ILP) : 0;
400 uint32_t bh = UINT32_MAX, bl = UINT32_MAX, bn = 0;
401 bool cand = false;
402 for (uint32_t k = 0; k < iters; k++) {
403 uint32_t n[SHA_ILP], nb[SHA_ILP], h[8][SHA_ILP];
404 for (uint32_t j = 0; j < SHA_ILP; j++) {
405 n[j] = nonce0 + th + threads * (k * SHA_ILP + j);
406 nb[j] = sha_bswap(n[j]);
407 }
408 sha_sha256d(mid, w0, w1, w2, nb, SHA_ILP, h);
409 for (uint32_t j = 0; j < SHA_ILP; j++) {
410 uint32_t hi = sha_le_word(h[7][j]), lo = sha_le_word(h[6][j]);
411 if (hi < bh || (hi == bh && lo < bl)) { bh = hi; bl = lo; bn = n[j]; }
412 cand |= hi <= t7;
413 }
414 }
415
416 /* The node's best (hi, lo, then the least nonce), the same on every thread; any candidate. */
417 uint64_t mine = (uint64_t)bh << 32 | bl;
418 uint64_t key = mesh_ctx_block_reduce_u64(ctx, mine, UINT64_MAX, mesh_rt_min_u64);
419 bn = mesh_ctx_block_reduce_u32(ctx, mine == key ? bn : UINT32_MAX, UINT32_MAX, mesh_rt_min_u32);
420 bool any = mesh_ctx_block_max_u32(ctx, cand ? 1u : 0u) != 0;
421 /* Warp 0 alone from here (no block collective follows). */
422 if (mesh_ctx_warp(ctx) != 0) return 0;
423 uint32_t best[8];
424 sha_hash1(mid, w0, w1, w2, bn, best);
425
426 bool stale = false;
427 if (lane == 0) {
428 sha_lock(v);
429 if (mesh_rt_ld_relaxed(gp) == g) {
430 uint64_t n = (uint64_t)1 << clog;
431 uint64_t old = mesh_view_u64(v, SHA_V_HASHES);
432 mesh_view_set_u64(v, SHA_V_HASHES, old + n);
433 mesh_ctx_count(ctx, 0, (uint32_t)((old + n) / 1000000 - old / 1000000));
434 mesh_ctx_count(ctx, 2, 1);
435 mesh_view_set_u32(v, SHA_V_SYSTEM, mesh_ctx_system(ctx));
436 uint32_t cur[8];
437 sha_read8(v, SHA_V_BEST, cur);
438 if (sha_cmp256(best, cur) < 0) {
439 sha_write8(v, SHA_V_BEST, best);
440 mesh_view_set_u32(v, SHA_V_BEST_ZBITS, sha_zbits_of(best));
441 mesh_view_set_u32(v, SHA_V_BEST_NONCE, bn);
442 mesh_view_set_u32(v, SHA_V_BEST_TIME, ntime);
443 mesh_view_set_u32(v, SHA_V_BEST_HI, best[7]);
444 }
445 } else {
446 stale = true;
447 mesh_ctx_count(ctx, 3, 1);
448 }
449 sha_unlock(v);
450 }
451 if (mesh_rt_shfl_idx(stale ? 1u : 0u, 0) != 0) return MESH_R_JOB_FAIL;
452
453 /* Shares: a full rescan of the chunk by warp 0 if any thread saw a
454 * candidate, one nonce per lane per step. */
455 if (any) {
456 for (uint32_t k = 0; k < (1u << clog) / 32; k++) {
457 uint32_t nonce = nonce0 + lane + 32 * k;
458 uint32_t hh[8];
459 sha_hash1(mid, w0, w1, w2, nonce, hh);
460 uint32_t bits = mesh_rt_ballot(sha_cmp256(hh, tgt) <= 0);
461 while (bits != 0) {
462 uint32_t src = (uint32_t)__builtin_ctz(bits);
463 bits &= bits - 1;
464 uint32_t n = mesh_rt_shfl_idx(nonce, src);
465 uint32_t sh[8];
466 for (uint32_t q = 0; q < 8; q++) sh[q] = mesh_rt_shfl_idx(hh[q], src);
467 if (lane == 0) {
468 sha_record_share(ctx, v, g, n, ntime, sh);
469 if (found < SHA_JOB_SHARES) {
470 mesh_view_set_u32(v, rec + 4 + 8 * found, n);
471 mesh_view_set_u32(v, rec + 8 + 8 * found, sha_zbits_of(sh));
472 }
473 found++;
474 }
475 }
476 }
477 }
478 /* The record, then the result (meshd reads the record once the completion is published). */
479 found = mesh_rt_shfl_idx(found, 0);
480 if (lane == 0) {
481 mesh_view_set_u32(v, rec, found);
482 for (uint32_t i = found; i < SHA_JOB_SHARES; i++) {
483 mesh_view_set_u32(v, rec + 4 + 8 * i, 0);
484 mesh_view_set_u32(v, rec + 8 + 8 * i, 0);
485 }
486 mesh_view_set_u32(v, rec + 4 + 8 * SHA_JOB_SHARES, ntime);
487 }
488 return found;
489}
490
491/* ---- request window (lane 0) */
492typedef struct {
493 mesh_view v;
494 uint32_t n;
495} sha_out;
496static void out_b(sha_out *o, uint8_t c) {
497 if (o->n < mesh_view_len(o->v)) { mesh_view_set_u8(o->v, o->n, c); o->n++; }
498}
499static void out_s(sha_out *o, const char *s) { for (; *s; s++) out_b(o, (uint8_t)*s); }
500static void out_dec(sha_out *o, uint64_t x) {
501 uint64_t d = 1;
502 while (x / d >= 10) d *= 10;
503 for (;;) {
504 out_b(o, (uint8_t)('0' + x / d));
505 x %= d;
506 if (d == 1) break;
507 d /= 10;
508 }
509}
510static void out_hex8(sha_out *o, uint8_t x) {
511 static const char hex[] = "0123456789abcdef";
512 out_b(o, (uint8_t)hex[(x >> 4) & 15]);
513 out_b(o, (uint8_t)hex[x & 15]);
514}
515static void out_kv(sha_out *o, const char *k, uint64_t x) {
516 out_b(o, ' ');
517 out_s(o, k);
518 out_b(o, '=');
519 out_dec(o, x);
520}
521
522static int sha_nib(uint8_t c) {
523 if (c >= '0' && c <= '9') return c - '0';
524 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
525 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
526 return -1;
527}
528static bool sha_is_ws(uint8_t c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == 0; }
529
530/* The next token of the request: start and length (0 at the end). */
531static void sha_token(mesh_view r, uint32_t *at, uint32_t *s, uint32_t *len) {
532 while (*at < mesh_view_len(r) && sha_is_ws(mesh_view_u8(r, *at))) (*at)++;
533 *s = *at;
534 while (*at < mesh_view_len(r) && !sha_is_ws(mesh_view_u8(r, *at))) (*at)++;
535 *len = *at - *s;
536}
537static bool sha_tok_is(mesh_view r, uint32_t s, uint32_t len, const char *w) {
538 uint32_t n = 0;
539 while (w[n]) n++;
540 if (len != n) return false;
541 for (uint32_t i = 0; i < len; i++)
542 if (mesh_view_u8(r, s + i) != (uint8_t)w[i]) return false;
543 return true;
544}
545static bool sha_tok_dec(mesh_view r, uint32_t s, uint32_t len, uint32_t *out) {
546 if (len == 0 || len > 10) return false;
547 uint64_t x = 0;
548 for (uint32_t i = 0; i < len; i++) {
549 uint8_t c = mesh_view_u8(r, s + i);
550 if (c < '0' || c > '9') return false;
551 x = x * 10 + (uint64_t)(c - '0');
552 }
553 if (x > UINT32_MAX) return false;
554 *out = (uint32_t)x;
555 return true;
556}
557static bool sha_tok_hex32(mesh_view r, uint32_t s, uint32_t len, uint32_t *out) {
558 if (len == 0 || len > 8) return false;
559 uint32_t x = 0;
560 for (uint32_t i = 0; i < len; i++) {
561 int d = sha_nib(mesh_view_u8(r, s + i));
562 if (d < 0) return false;
563 x = x << 4 | (uint32_t)d;
564 }
565 *out = x;
566 return true;
567}
568/* Hex bytes of the token into the view at dst (n bytes; rev: reversed, display order). */
569static bool sha_tok_bytes(mesh_view r, uint32_t s, uint32_t len, mesh_view v, uint32_t dst, uint32_t n, bool rev) {
570 if (len != 2 * n) return false;
571 for (uint32_t i = 0; i < n; i++) {
572 int a = sha_nib(mesh_view_u8(r, s + 2 * i)), b = sha_nib(mesh_view_u8(r, s + 2 * i + 1));
573 if (a < 0 || b < 0) return false;
574 mesh_view_set_u8(v, dst + (rev ? n - 1 - i : i), (uint8_t)(a << 4 | b));
575 }
576 return true;
577}
578
579#define SHA_T_NONE 0u
580#define SHA_T_ZBITS 1u
581#define SHA_T_NBITS 2u
582#define SHA_T_BYTES 3u
583
584/* Parses the whole request (into V_STAGE), then applies it as one change. */
585__attribute__((noinline)) static uint32_t sha_request(const mesh_ctx *ctx) {
586 mesh_view r = mesh_ctx_request(ctx), v = mesh_ctx_view(ctx);
587 sha_out o = {mesh_ctx_response(ctx), 0};
588 bool hdr = false, has_base = false, has_time = false, genesis = false, reset = false, status = false;
589 uint32_t tk = SHA_T_NONE, tv = 0, base = 0, time = 0;
590 const char *err = NULL;
591 uint8_t first = mesh_view_u8(r, 0);
592 if ((mesh_view_len(r) == 80 || mesh_view_len(r) == 112) && !(first >= 'a' && first <= 'z')) {
593 for (uint32_t i = 0; i < mesh_view_len(r); i++) mesh_view_set_u8(v, SHA_V_STAGE + i, mesh_view_u8(r, i));
594 hdr = true;
595 if (mesh_view_len(r) == 112) tk = SHA_T_BYTES;
596 } else {
597 uint32_t at = 0;
598 for (;;) {
599 uint32_t s, n, as, an;
600 sha_token(r, &at, &s, &n);
601 if (n == 0) break;
602 if (sha_tok_is(r, s, n, "header")) {
603 sha_token(r, &at, &as, &an);
604 if (!sha_tok_bytes(r, as, an, v, SHA_V_STAGE, 80, false)) { err = "header: 160 hex digits"; break; }
605 hdr = true;
606 } else if (sha_tok_is(r, s, n, "target")) {
607 sha_token(r, &at, &as, &an);
608 if (!sha_tok_bytes(r, as, an, v, SHA_V_STAGE + 80, 32, true)) { err = "target: 64 hex digits"; break; }
609 tk = SHA_T_BYTES;
610 } else if (sha_tok_is(r, s, n, "zbits")) {
611 uint32_t z;
612 sha_token(r, &at, &as, &an);
613 if (sha_tok_dec(r, as, an, &z) && z >= 1 && z <= 255) { tk = SHA_T_ZBITS; tv = z; }
614 else { err = "zbits: 1..=255"; break; }
615 } else if (sha_tok_is(r, s, n, "nbits")) {
616 uint32_t b;
617 sha_token(r, &at, &as, &an);
618 if (sha_tok_hex32(r, as, an, &b)) { tk = SHA_T_NBITS; tv = b; }
619 else { err = "nbits: 8 hex digits"; break; }
620 } else if (sha_tok_is(r, s, n, "base")) {
621 sha_token(r, &at, &as, &an);
622 if (sha_tok_dec(r, as, an, &base)) has_base = true;
623 else { err = "base: a u32"; break; }
624 } else if (sha_tok_is(r, s, n, "time")) {
625 sha_token(r, &at, &as, &an);
626 if (sha_tok_dec(r, as, an, &time)) has_time = true;
627 else { err = "time: a u32"; break; }
628 } else if (sha_tok_is(r, s, n, "chunk")) {
629 err = "chunk: fixed by the manifest's param (one chunk size on every member of a cluster)";
630 break;
631 } else if (sha_tok_is(r, s, n, "genesis")) {
632 genesis = true;
633 } else if (sha_tok_is(r, s, n, "reset")) {
634 reset = true;
635 } else if (sha_tok_is(r, s, n, "status")) {
636 status = true;
637 } else {
638 err = "unknown word (header target zbits nbits base time genesis reset status)";
639 break;
640 }
641 }
642 }
643 if (err) {
644 out_s(&o, "err ");
645 out_s(&o, err);
646 out_b(&o, '\n');
647 return o.n;
648 }
649 uint32_t clog;
650 if (!sha_chunk_log2(ctx, &clog)) {
651 out_s(&o, "err the manifest's param (chunk_log2) is outside 6..=24\n");
652 return o.n;
653 }
654 sha_sync_chunk(v, clog);
655 /* A pure `status` changes nothing (and keeps gen: no in-flight chunk goes stale). */
656 bool change = genesis || hdr || has_time || has_base || tk != SHA_T_NONE || reset;
657 bool ok = true;
658 if (change) {
659 uint32_t g = sha_config_begin(v);
660 if (genesis) sha_set_genesis(v);
661 if (hdr)
662 for (uint32_t i = 0; i < 80; i++) mesh_view_set_u8(v, SHA_V_HEADER + i, mesh_view_u8(v, SHA_V_STAGE + i));
663 if (has_time) mesh_view_set_u32(v, SHA_V_HEADER + 68, time);
664 if (has_base) mesh_view_set_u32(v, SHA_V_BASE, base);
665 if (hdr || has_time) sha_header_changed(v);
666 else if (has_base) sha_restart(v);
667 if (tk == SHA_T_ZBITS) sha_set_zbits(v, tv);
668 else if (tk == SHA_T_NBITS) ok = sha_set_nbits(v, tv);
669 else if (tk == SHA_T_BYTES) {
670 for (uint32_t i = 0; i < 32; i++) mesh_view_set_u8(v, SHA_V_TARGET + i, mesh_view_u8(v, SHA_V_STAGE + 80 + i));
671 sha_target_changed(v);
672 }
673 if (reset) sha_reset_stats(v);
674 sha_config_end(v, g);
675 }
676 if (!ok) {
677 out_s(&o, "err nbits: negative or overflowing target\n");
678 return o.n;
679 }
680 out_s(&o, "ok");
681 out_kv(&o, "gen", mesh_rt_ld_relaxed(mesh_view_ptr(v) + SHA_V_GEN));
682 out_kv(&o, "zbits", mesh_view_u32(v, SHA_V_TARGET_ZBITS));
683 out_kv(&o, "base", mesh_view_u32(v, SHA_V_BASE));
684 out_kv(&o, "chunk_log2", mesh_view_u32(v, SHA_V_CHUNK_LOG2));
685 out_kv(&o, "time", mesh_view_u32(v, SHA_V_TIME));
686 if (status) {
687 out_kv(&o, "hashes", mesh_view_u64(v, SHA_V_HASHES));
688 out_kv(&o, "shares", mesh_view_u32(v, SHA_V_SHARES));
689 out_kv(&o, "best_zbits", mesh_view_u32(v, SHA_V_BEST_ZBITS));
690 out_kv(&o, "best_nonce", mesh_view_u32(v, SHA_V_BEST_NONCE));
691 out_s(&o, " best=");
692 for (int i = 31; i >= 0; i--) out_hex8(&o, mesh_view_u8(v, SHA_V_BEST + (uint32_t)i));
693 }
694 out_b(&o, '\n');
695 return o.n;
696}
697
698static uint32_t sha_task(const mesh_ctx *ctx, uint32_t op, uint32_t arg) {
699 if (op == MESH_OP_JOB) return sha_grind(ctx, arg);
700 /* Everything but the grind runs on warp 0. */
701 if (mesh_ctx_warp(ctx) != 0) return MESH_R_NONE;
702 if (op == MESH_OP_REQ) {
703 uint32_t n = mesh_ctx_leader(ctx) ? sha_request(ctx) : 0;
704 n = mesh_rt_shfl_idx(n, 0);
705 mesh_ctx_respond(ctx, n);
706 return n;
707 }
708 if (op >= SHA_OP_ZBITS && op <= SHA_OP_TIME) {
709 if (!mesh_ctx_leader(ctx)) return MESH_R_NONE;
710 mesh_view v = mesh_ctx_view(ctx);
711 uint32_t c;
712 if (sha_chunk_log2(ctx, &c)) sha_sync_chunk(v, c);
713 bool ok = false;
714 uint32_t g;
715 switch (op) {
716 case SHA_OP_ZBITS:
717 if (arg >= 1 && arg <= 255) { g = sha_config_begin(v); sha_set_zbits(v, arg); sha_config_end(v, g); ok = true; }
718 break;
719 case SHA_OP_RESET: g = sha_config_begin(v); sha_reset_stats(v); sha_config_end(v, g); ok = true; break;
720 case SHA_OP_NBITS: g = sha_config_begin(v); ok = sha_set_nbits(v, arg); sha_config_end(v, g); break;
721 case SHA_OP_GENESIS:
722 if (arg <= 255) {
723 g = sha_config_begin(v);
724 sha_set_genesis(v);
725 if (arg == 0) ok = sha_set_nbits(v, mesh_view_u32(v, SHA_V_NBITS));
726 else { sha_set_zbits(v, arg); ok = true; }
727 sha_config_end(v, g);
728 }
729 break;
730 case SHA_OP_BASE: g = sha_config_begin(v); mesh_view_set_u32(v, SHA_V_BASE, arg); sha_restart(v); sha_config_end(v, g); ok = true; break;
731 case SHA_OP_TIME: g = sha_config_begin(v); mesh_view_set_u32(v, SHA_V_HEADER + 68, arg); sha_header_changed(v); sha_config_end(v, g); ok = true; break;
732 default: break; /* OP_CHUNK: refused */
733 }
734 return ok ? MESH_R_NONE : MESH_R_BAD_OP;
735 }
736 return MESH_R_BAD_OP;
737}
738
739MESH_PROGRAM(sha_init, sha_task);