==> src/sha256d.c <==1/* sha256d for the wasm executor (alpha-rust-mesh programs/sha256d/wasm): Bitcoin-style double
2 * SHA-256 nonce grinding, the same program as programs/sha256d -- the same view layout (V_*,
3 * mirrored by kcheck's sha checks), ops, request language, configuration seqlock (gen), spin lock
4 * and share ring -- on one CPU thread per node instead of a warp.
5 *
6 * A job program (plan F5): OP_JOB grinds the leased chunk g = V_JOB_BASE + arg of 1 << chunk_log2
7 * nonces (the manifest's param). The hot loop hashes the chunk's nonces in order with the header's
8 * midstate (the first block's compression, kept in the view) and keeps the best (hash top 64 bits,
9 * nonce) and every share (hash <= target) in linear memory; then, under the lock and only if gen is
10 * unchanged, it adds the hashes, the best and the shares in nonce order -- what the GPU image
11 * records. A chunk that straddled a configuration change returns R_JOB_FAIL (the leader retries it)
12 * and records nothing but the stale count. The item's record goes to V_JOB_OUT + 32 (ticket % 64). */
13#include "mesh/abi/ring.h"
14#include "mesh/rt_wasm.h"
15#include "sha256_compress.h"
16
17/* ---- view layout (programs/sha256d) ---- */
18#define V_MAGIC 0x000u
19#define V_GEN 0x004u
20#define V_LOCK 0x008u
21#define V_FOUND 0x00cu
22#define V_HASHES 0x010u
23#define V_BASE 0x020u
24#define V_CHUNK_LOG2 0x024u
25#define V_TARGET_ZBITS 0x028u
26#define V_SHARES 0x02cu
27#define V_BEST_ZBITS 0x030u
28#define V_BEST_NONCE 0x034u
29#define V_BEST_TIME 0x038u
30#define V_LAST_NONCE 0x03cu
31#define V_LAST_TIME 0x040u
32#define V_VERSION 0x044u
33#define V_TIME 0x048u
34#define V_NBITS 0x04cu
35#define V_BEST_HI 0x050u
36#define V_SYSTEM 0x054u
37#define V_HEADERS 0x058u
38#define V_LAST_ZBITS 0x05cu
39#define V_BEST 0x060u
40#define V_HEADER 0x080u
41#define V_TARGET 0x0d0u
42#define V_MID 0x0f0u
43#define V_STAGE 0x110u
44#define V_RING 0x200u
45#define RING 32u
46#define RING_BYTES 48u
47#define V_JOB_BASE 0x1000u
48#define V_JOB_OUT 0x1800u
49#define JOB_REC 32u
50#define JOB_SHARES 3u
51#define VIEW_MIN 0x2000u
52#define MAGIC 0x36353253u /* "S256" */
53
54/* The GPU image's chunk bounds: one chunk size on every member. */
55#define CHUNK_LOG2_MIN 6u
56#define CHUNK_LOG2_MAX 24u
57#define ZBITS_DEFAULT 26u
58
59#define OP_ZBITS 3u
60#define OP_RESET 4u
61#define OP_NBITS 5u
62#define OP_GENESIS 6u
63#define OP_CHUNK 7u
64#define OP_BASE 8u
65#define OP_TIME 9u
66
67static const uint32_t GENESIS[20] = {
68 0x00000001, 0, 0, 0, 0, 0, 0, 0, 0, 0xfdeda33b, 0xb2127b7a, 0x3e2cc77a, 0x618f7667, 0xc31bc87f,
69 0x32518a88, 0xaab89f3a, 0x4a5e1e4b, 0x495fab29, 0x1d00ffff, 0x7c2bac1d,
70};
71
72static uint32_t bswap(uint32_t x) { return x >> 24 | (x >> 8 & 0xff00u) | (x << 8 & 0xff0000u) | x << 24; }
73
74/* SHA256d of a header that differs from the midstate's only in its last 16 bytes: w0..w2 the
75 * second block's first words (big-endian), the nonce. The hash as a little-endian 256-bit number
76 * (words, 7 most significant). */
77static void hash(const uint32_t mid[8], uint32_t w0, uint32_t w1, uint32_t w2, uint32_t nonce, uint32_t out[8]) {
78 uint32_t h[8], w[16] = {0};
79 for (int k = 0; k < 8; k++) h[k] = mid[k];
80 w[0] = w0;
81 w[1] = w1;
82 w[2] = w2;
83 w[3] = bswap(nonce);
84 w[4] = 0x80000000u;
85 w[15] = 640;
86 sha256_compress(h, w);
87 uint32_t v2[16] = {0}, h2[8];
88 for (int k = 0; k < 8; k++) v2[k] = h[k], h2[k] = SHA256_H0[k];
89 v2[8] = 0x80000000u;
90 v2[15] = 256;
91 sha256_compress(h2, v2);
92 for (int k = 0; k < 8; k++) out[k] = bswap(h2[k]);
93}
94
95/* ---- lock, configuration ---- */
96static void lock(mesh_view v) {
97 while (mesh_view_cas32(v, V_LOCK, 0, 1) != 0) {
98 }
99}
100static void unlock(mesh_view v) { mesh_view_set_u32(v, V_LOCK, 0); }
101/* A configuration change: locked, gen odd meanwhile. */
102static uint32_t config_begin(mesh_view v) {
103 lock(v);
104 uint32_t g = mesh_view_u32(v, V_GEN) | 1u;
105 mesh_view_set_u32(v, V_GEN, g);
106 return g;
107}
108static void config_end(mesh_view v, uint32_t g) {
109 mesh_view_set_u32(v, V_GEN, g + 1u);
110 unlock(v);
111}
112
113static uint32_t zbits_of(const uint32_t w[8]) {
114 uint32_t z = 0;
115 for (int k = 7; k >= 0; k--) {
116 if (w[k] == 0) z += 32;
117 else return z + (uint32_t)__builtin_clz(w[k]);
118 }
119 return z;
120}
121static void read8(mesh_view v, uint32_t off, uint32_t w[8]) { mesh_view_read_u32s(v, off, w, 8); }
122static void write8(mesh_view v, uint32_t off, const uint32_t w[8]) { mesh_view_write_u32s(v, off, w, 8); }
123/* Little-endian 256-bit compare: the most significant differing word decides. */
124static int cmp256(const uint32_t a[8], const uint32_t b[8]) {
125 for (int k = 7; k >= 0; k--)
126 if (a[k] != b[k]) return a[k] < b[k] ? -1 : 1;
127 return 0;
128}
129
130static void set_midstate(mesh_view v) {
131 uint32_t hb[16], w[16], s[8];
132 mesh_view_read_u32s(v, V_HEADER, hb, 16);
133 for (int k = 0; k < 16; k++) w[k] = bswap(hb[k]);
134 for (int k = 0; k < 8; k++) s[k] = SHA256_H0[k];
135 sha256_compress(s, w);
136 write8(v, V_MID, s);
137}
138
139static const uint8_t zero_ring[RING * RING_BYTES];
140
141static void clear_shares(mesh_view v) {
142 const uint32_t offs[5] = {V_SHARES, V_FOUND, V_LAST_NONCE, V_LAST_TIME, V_LAST_ZBITS};
143 for (int i = 0; i < 5; i++) mesh_view_set_u32(v, offs[i], 0);
144 mesh_view_write(v, V_RING, zero_ring, sizeof zero_ring);
145}
146
147static void clear_best(mesh_view v) {
148 const uint32_t ones[8] = {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
149 write8(v, V_BEST, ones);
150 mesh_view_set_u32(v, V_BEST_ZBITS, 0);
151 mesh_view_set_u32(v, V_BEST_NONCE, 0);
152 mesh_view_set_u32(v, V_BEST_TIME, 0);
153 mesh_view_set_u32(v, V_BEST_HI, UINT32_MAX);
154}
155
156static void restart(mesh_view v) {
157 clear_best(v);
158 clear_shares(v);
159}
160
161static void header_changed(mesh_view v) {
162 mesh_view_set_u32(v, V_VERSION, mesh_view_u32(v, V_HEADER));
163 mesh_view_set_u32(v, V_TIME, mesh_view_u32(v, V_HEADER + 68));
164 mesh_view_set_u32(v, V_NBITS, mesh_view_u32(v, V_HEADER + 72));
165 mesh_view_set_u32(v, V_HEADERS, mesh_view_u32(v, V_HEADERS) + 1u);
166 set_midstate(v);
167 restart(v);
168}
169
170static void target_changed(mesh_view v) {
171 uint32_t t[8];
172 read8(v, V_TARGET, t);
173 mesh_view_set_u32(v, V_TARGET_ZBITS, zbits_of(t));
174 clear_shares(v);
175}
176
177static void set_zbits(mesh_view v, uint32_t z) {
178 uint32_t lim = 256 - z, w[8];
179 for (uint32_t k = 0; k < 8; k++) {
180 uint32_t lo = 32 * k;
181 w[k] = lo + 32 <= lim ? UINT32_MAX : lo >= lim ? 0 : (1u << (lim - lo)) - 1u;
182 }
183 write8(v, V_TARGET, w);
184 target_changed(v);
185}
186
187static bool set_nbits(mesh_view v, uint32_t bits) {
188 uint32_t exp = bits >> 24, mant = bits & 0x007fffffu;
189 if ((bits & 0x00800000u) || exp > 32) return false;
190 int32_t pos = (int32_t)exp - 3;
191 if (pos < 0) {
192 mant >>= 8 * (uint32_t)(-pos);
193 pos = 0;
194 }
195 const uint32_t zero[8] = {0};
196 write8(v, V_TARGET, zero);
197 for (uint32_t i = 0; i < 3; i++) {
198 uint32_t b = (mant >> (8 * i)) & 0xffu;
199 uint32_t at = (uint32_t)pos + i;
200 if (at < 32) mesh_view_set_u8(v, V_TARGET + at, (uint8_t)b);
201 else if (b != 0) return false;
202 }
203 target_changed(v);
204 return true;
205}
206
207static void set_genesis(mesh_view v) {
208 mesh_view_write_u32s(v, V_HEADER, GENESIS, 20);
209 mesh_view_set_u32(v, V_BASE, 0);
210 header_changed(v);
211}
212
213static void reset_stats(mesh_view v) {
214 mesh_view_set_u64(v, V_HASHES, 0);
215 clear_best(v);
216 clear_shares(v);
217}
218
219static void init(const mesh_ctx *ctx) {
220 mesh_view v = mesh_ctx_view(ctx);
221 /* A view smaller than the layout faults here (bounds-checked), loudly. */
222 if (mesh_view_len(v) < VIEW_MIN) {
223 (void)mesh_view_u32(v, VIEW_MIN - 4);
224 return;
225 }
226 uint32_t g = config_begin(v);
227 mesh_view_set_u32(v, V_MAGIC, MAGIC);
228 mesh_view_set_u32(v, V_CHUNK_LOG2, mesh_ctx_param(ctx));
229 mesh_view_set_u32(v, V_SYSTEM, mesh_ctx_system(ctx));
230 set_genesis(v);
231 set_zbits(v, ZBITS_DEFAULT);
232 reset_stats(v);
233 config_end(v, g);
234}
235
236/* ---- grinding ---- */
237static bool chunk_log2(const mesh_ctx *ctx, uint32_t *c) {
238 uint32_t p = mesh_ctx_param(ctx);
239 if (p < CHUNK_LOG2_MIN || p > CHUNK_LOG2_MAX) return false;
240 *c = p;
241 return true;
242}
243
244/* If the param changed since the search began (an upgrade), restart at chunk 0 with it. */
245static void sync_chunk(mesh_view v, uint32_t clog) {
246 if (mesh_view_u32(v, V_CHUNK_LOG2) != clog) {
247 uint32_t g = config_begin(v);
248 if (mesh_view_u32(v, V_CHUNK_LOG2) != clog) {
249 mesh_view_set_u32(v, V_CHUNK_LOG2, clog);
250 restart(v);
251 }
252 config_end(v, g);
253 }
254}
255
256/* The shares a chunk found (more than the ring holds are counted, the ring keeps the last RING). */
257static uint32_t sh_nonce[RING], sh_hash[RING][8];
258
259/* One leased chunk (OP_JOB: chunk V_JOB_BASE + job): hash, record. */
260static uint32_t grind(const mesh_ctx *ctx, uint32_t job) {
261 mesh_view v = mesh_ctx_view(ctx);
262 uint32_t clog;
263 if (!chunk_log2(ctx, &clog)) return MESH_R_JOB_FAIL;
264 sync_chunk(v, clog);
265 /* Configuration under the gen seqlock. */
266 uint32_t g, mid[8], hw[3], base, tgt[8];
267 for (;;) {
268 uint32_t g1 = mesh_view_u32(v, V_GEN);
269 while (g1 & 1) g1 = mesh_view_u32(v, V_GEN);
270 read8(v, V_MID, mid);
271 mesh_view_read_u32s(v, V_HEADER + 64, hw, 3);
272 base = mesh_view_u32(v, V_BASE);
273 read8(v, V_TARGET, tgt);
274 if (mesh_view_u32(v, V_GEN) == g1) {
275 g = g1;
276 break;
277 }
278 /* Read across a change: again under the new gen. */
279 }
280 uint64_t gidx = mesh_view_u64(v, V_JOB_BASE) + job;
281 uint64_t p = gidx << clog;
282 uint32_t nonce0 = base + (uint32_t)p;
283 uint32_t ntime = hw[1] + (uint32_t)(p >> 32);
284 uint32_t w0 = bswap(hw[0]), w1 = bswap(ntime), w2 = bswap(hw[2]);
285
286 /* The hot loop: linear memory only. */
287 uint32_t best[8] = {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
288 uint32_t bn = 0, n_sh = 0;
289 for (uint32_t i = 0; i < 1u << clog; i++) {
290 uint32_t nonce = nonce0 + i, h[8];
291 hash(mid, w0, w1, w2, nonce, h);
292 /* The GPU's per-lane best: the top 64 bits, the first nonce on a tie. */
293 if (h[7] < best[7] || (h[7] == best[7] && h[6] < best[6])) {
294 for (int k = 0; k < 8; k++) best[k] = h[k];
295 bn = nonce;
296 }
297 if (h[7] <= tgt[7] && cmp256(h, tgt) <= 0) {
298 uint32_t r = n_sh % RING;
299 sh_nonce[r] = nonce;
300 for (int k = 0; k < 8; k++) sh_hash[r][k] = h[k];
301 n_sh++;
302 }
303 }
304
305 /* Stats, best and shares in one critical section, only if the configuration is still g. */
306 lock(v);
307 if (mesh_view_u32(v, V_GEN) != g) {
308 mesh_ctx_count(ctx, 3, 1);
309 unlock(v);
310 return MESH_R_JOB_FAIL;
311 }
312 uint64_t n = 1ull << clog;
313 uint64_t old = mesh_view_u64(v, V_HASHES);
314 mesh_view_set_u64(v, V_HASHES, old + n);
315 mesh_ctx_count(ctx, 0, (uint32_t)((old + n) / 1000000u - old / 1000000u));
316 mesh_ctx_count(ctx, 2, 1);
317 mesh_view_set_u32(v, V_SYSTEM, mesh_ctx_system(ctx));
318 uint32_t cur[8];
319 read8(v, V_BEST, cur);
320 if (cmp256(best, cur) < 0) {
321 write8(v, V_BEST, best);
322 mesh_view_set_u32(v, V_BEST_ZBITS, zbits_of(best));
323 mesh_view_set_u32(v, V_BEST_NONCE, bn);
324 mesh_view_set_u32(v, V_BEST_TIME, ntime);
325 mesh_view_set_u32(v, V_BEST_HI, best[7]);
326 }
327 if (n_sh > 0) {
328 /* In nonce order, as the GPU records them: share s at ring entry s mod RING (the chunk kept
329 * only its last RING; the earlier ones are counted). */
330 uint32_t s0 = mesh_view_u32(v, V_SHARES);
331 uint32_t first = n_sh > RING ? n_sh - RING : 0;
332 for (uint32_t i = first; i < n_sh; i++) {
333 uint32_t nonce = sh_nonce[i % RING];
334 const uint32_t *h = sh_hash[i % RING];
335 uint32_t s = s0 + i, z = zbits_of(h);
336 uint32_t e = V_RING + (s % RING) * RING_BYTES;
337 const uint32_t head[4] = {nonce, ntime, s + 1u, z};
338 mesh_view_write_u32s(v, e, head, 4);
339 write8(v, e + 16, h);
340 mesh_view_set_u32(v, V_LAST_NONCE, nonce);
341 mesh_view_set_u32(v, V_LAST_ZBITS, z);
342 }
343 mesh_view_set_u32(v, V_SHARES, s0 + n_sh);
344 mesh_view_set_u32(v, V_LAST_TIME, ntime);
345 mesh_view_set_u32(v, V_FOUND, 1);
346 mesh_ctx_count(ctx, 1, n_sh);
347 }
348 unlock(v);
349 /* The item's record: the shares in nonce order (the first JOB_SHARES), the chunk's ntime. */
350 uint32_t rec = V_JOB_OUT + JOB_REC * (mesh_ctx_ticket(ctx) % MESH_PROG_RESULTS);
351 uint32_t w[2 + 2 * JOB_SHARES] = {0};
352 w[0] = n_sh;
353 uint32_t k = n_sh < JOB_SHARES ? n_sh : JOB_SHARES;
354 for (uint32_t i = 0; i < k; i++) {
355 w[1 + 2 * i] = sh_nonce[i];
356 w[2 + 2 * i] = zbits_of(sh_hash[i]);
357 }
358 w[1 + 2 * JOB_SHARES] = ntime;
359 mesh_view_write_u32s(v, rec, w, 2 + 2 * JOB_SHARES);
360 return n_sh;
361}
362
363/* ---- request window ---- */
364typedef struct {
365 uint8_t buf[512];
366 uint32_t n, max;
367} out_t;
368
369static void ob(out_t *o, uint8_t c) {
370 uint32_t cap = o->max < sizeof o->buf ? o->max : (uint32_t)sizeof o->buf;
371 if (o->n < cap) o->buf[o->n++] = c;
372}
373static void os(out_t *o, const char *s) {
374 while (*s) ob(o, (uint8_t)*s++);
375}
376static void odec(out_t *o, uint64_t x) {
377 uint64_t d = 1;
378 while (x / d >= 10) d *= 10;
379 for (;;) {
380 ob(o, (uint8_t)('0' + x / d));
381 x %= d;
382 if (d == 1) break;
383 d /= 10;
384 }
385}
386static void ohex8(out_t *o, uint8_t x) {
387 static const char hex[] = "0123456789abcdef";
388 ob(o, (uint8_t)hex[(x >> 4) & 15]);
389 ob(o, (uint8_t)hex[x & 15]);
390}
391static void okv(out_t *o, const char *k, uint64_t x) {
392 ob(o, ' ');
393 os(o, k);
394 ob(o, '=');
395 odec(o, x);
396}
397
398static int nib(uint8_t c) {
399 if (c >= '0' && c <= '9') return c - '0';
400 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
401 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
402 return -1;
403}
404static bool is_ws(uint8_t c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == 0; }
405
406/* The next token of r: start and length; length 0 at the end. */
407static void token(const uint8_t *r, uint32_t len, uint32_t *at, uint32_t *s, uint32_t *n) {
408 while (*at < len && is_ws(r[*at])) (*at)++;
409 *s = *at;
410 while (*at < len && !is_ws(r[*at])) (*at)++;
411 *n = *at - *s;
412}
413static bool tok_is(const uint8_t *t, uint32_t n, const char *w) {
414 uint32_t k = 0;
415 while (w[k]) k++;
416 if (k != n) return false;
417 for (uint32_t i = 0; i < n; i++)
418 if (t[i] != (uint8_t)w[i]) return false;
419 return true;
420}
421static bool tok_dec(const uint8_t *t, uint32_t n, uint32_t *out) {
422 if (n == 0 || n > 10) return false;
423 uint64_t x = 0;
424 for (uint32_t i = 0; i < n; i++) {
425 if (t[i] < '0' || t[i] > '9') return false;
426 x = x * 10 + (uint64_t)(t[i] - '0');
427 }
428 if (x > UINT32_MAX) return false;
429 *out = (uint32_t)x;
430 return true;
431}
432static bool tok_hex32(const uint8_t *t, uint32_t n, uint32_t *out) {
433 if (n == 0 || n > 8) return false;
434 uint32_t x = 0;
435 for (uint32_t i = 0; i < n; i++) {
436 int d = nib(t[i]);
437 if (d < 0) return false;
438 x = x << 4 | (uint32_t)d;
439 }
440 *out = x;
441 return true;
442}
443/* Hex bytes of token t into dst (rev: reversed, display order). */
444static bool tok_bytes(const uint8_t *t, uint32_t n, uint8_t *dst, uint32_t len, bool rev) {
445 if (n != 2 * len) return false;
446 for (uint32_t i = 0; i < len; i++) {
447 int a = nib(t[2 * i]), b = nib(t[2 * i + 1]);
448 if (a < 0 || b < 0) return false;
449 dst[rev ? len - 1 - i : i] = (uint8_t)(a << 4 | b);
450 }
451 return true;
452}
453
454#define T_NONE 0u
455#define T_ZBITS 1u
456#define T_NBITS 2u
457#define T_BYTES 3u
458
459static uint8_t rb[512];
460
461static uint32_t reply(const mesh_ctx *ctx, out_t *o) {
462 mesh_view_write(mesh_ctx_response(ctx), 0, o->buf, o->n);
463 return o->n;
464}
465
466/* Parses the whole request (into V_STAGE), then applies it as one change. */
467static uint32_t request(const mesh_ctx *ctx) {
468 mesh_view rv = mesh_ctx_request(ctx);
469 uint32_t rlen = mesh_view_len(rv) < sizeof rb ? mesh_view_len(rv) : (uint32_t)sizeof rb;
470 mesh_view_read(rv, 0, rb, rlen);
471 const uint8_t *r = rb;
472 mesh_view v = mesh_ctx_view(ctx);
473 out_t o = {{0}, 0, mesh_view_len(mesh_ctx_response(ctx))};
474 bool hdr = false, genesis = false, reset = false, status = false, has_base = false, has_time = false;
475 uint32_t tk = T_NONE, tv = 0, base = 0, time = 0;
476 uint8_t stage[112] = {0};
477 const char *err = NULL;
478 uint8_t first = rlen ? r[0] : 0;
479 if ((rlen == 80 || rlen == 112) && !(first >= 'a' && first <= 'z')) {
480 for (uint32_t i = 0; i < rlen; i++) stage[i] = r[i];
481 hdr = true;
482 if (rlen == 112) tk = T_BYTES;
483 } else {
484 uint32_t at = 0;
485 for (;;) {
486 uint32_t ts, tn, as, an;
487 token(r, rlen, &at, &ts, &tn);
488 if (tn == 0) break;
489 const uint8_t *w = r + ts;
490 token(r, rlen, &at, &as, &an);
491 const uint8_t *arg = r + as;
492 bool used = true;
493 if (tok_is(w, tn, "header")) {
494 if (!tok_bytes(arg, an, stage, 80, false)) { err = "header: 160 hex digits"; break; }
495 hdr = true;
496 } else if (tok_is(w, tn, "target")) {
497 if (!tok_bytes(arg, an, stage + 80, 32, true)) { err = "target: 64 hex digits"; break; }
498 tk = T_BYTES;
499 } else if (tok_is(w, tn, "zbits")) {
500 uint32_t z;
501 if (!tok_dec(arg, an, &z) || z < 1 || z > 255) { err = "zbits: 1..=255"; break; }
502 tk = T_ZBITS;
503 tv = z;
504 } else if (tok_is(w, tn, "nbits")) {
505 uint32_t b;
506 if (!tok_hex32(arg, an, &b)) { err = "nbits: 8 hex digits"; break; }
507 tk = T_NBITS;
508 tv = b;
509 } else if (tok_is(w, tn, "base")) {
510 if (!tok_dec(arg, an, &base)) { err = "base: a u32"; break; }
511 has_base = true;
512 } else if (tok_is(w, tn, "time")) {
513 if (!tok_dec(arg, an, &time)) { err = "time: a u32"; break; }
514 has_time = true;
515 } else if (tok_is(w, tn, "chunk")) {
516 err = "chunk: fixed by the manifest's param (one chunk size on every member of a cluster)";
517 break;
518 } else if (tok_is(w, tn, "genesis")) {
519 genesis = true;
520 used = false;
521 } else if (tok_is(w, tn, "reset")) {
522 reset = true;
523 used = false;
524 } else if (tok_is(w, tn, "status")) {
525 status = true;
526 used = false;
527 } else {
528 err = "unknown word (header target zbits nbits base time genesis reset status)";
529 break;
530 }
531 /* A word without an argument gives the token back. */
532 if (!used) at = as;
533 }
534 }
535 if (err) {
536 os(&o, "err ");
537 os(&o, err);
538 ob(&o, '\n');
539 return reply(ctx, &o);
540 }
541 uint32_t clog;
542 if (!chunk_log2(ctx, &clog)) {
543 os(&o, "err the manifest's param (chunk_log2) is outside 6..=24\n");
544 return reply(ctx, &o);
545 }
546 sync_chunk(v, clog);
547 if (hdr || tk == T_BYTES) mesh_view_write(v, V_STAGE, stage, sizeof stage);
548 /* A pure `status` changes nothing (and keeps gen: no in-flight chunk goes stale). */
549 bool change = genesis || hdr || has_time || has_base || tk != T_NONE || reset;
550 bool ok = true;
551 if (change) {
552 uint32_t g = config_begin(v);
553 if (genesis) set_genesis(v);
554 if (hdr) {
555 uint8_t h[80];
556 mesh_view_read(v, V_STAGE, h, 80);
557 mesh_view_write(v, V_HEADER, h, 80);
558 }
559 if (has_time) mesh_view_set_u32(v, V_HEADER + 68, time);
560 if (has_base) mesh_view_set_u32(v, V_BASE, base);
561 if (hdr || has_time) header_changed(v);
562 else if (has_base) restart(v);
563 switch (tk) {
564 case T_ZBITS: set_zbits(v, tv); break;
565 case T_NBITS: ok = set_nbits(v, tv); break;
566 case T_BYTES: {
567 uint8_t t[32];
568 mesh_view_read(v, V_STAGE + 80, t, 32);
569 mesh_view_write(v, V_TARGET, t, 32);
570 target_changed(v);
571 break;
572 }
573 default: break;
574 }
575 if (reset) reset_stats(v);
576 config_end(v, g);
577 }
578 if (!ok) {
579 os(&o, "err nbits: negative or overflowing target\n");
580 } else {
581 os(&o, "ok");
582 okv(&o, "gen", mesh_view_u32(v, V_GEN));
583 okv(&o, "zbits", mesh_view_u32(v, V_TARGET_ZBITS));
584 okv(&o, "base", mesh_view_u32(v, V_BASE));
585 okv(&o, "chunk_log2", mesh_view_u32(v, V_CHUNK_LOG2));
586 okv(&o, "time", mesh_view_u32(v, V_TIME));
587 if (status) {
588 okv(&o, "hashes", mesh_view_u64(v, V_HASHES));
589 okv(&o, "shares", mesh_view_u32(v, V_SHARES));
590 okv(&o, "best_zbits", mesh_view_u32(v, V_BEST_ZBITS));
591 okv(&o, "best_nonce", mesh_view_u32(v, V_BEST_NONCE));
592 os(&o, " best=");
593 uint8_t b[32];
594 mesh_view_read(v, V_BEST, b, 32);
595 for (int i = 31; i >= 0; i--) ohex8(&o, b[i]);
596 }
597 ob(&o, '\n');
598 }
599 return reply(ctx, &o);
600}
601
602/* ---- ops ---- */
603static uint32_t task(const mesh_ctx *ctx, uint32_t op, uint32_t arg) {
604 switch (op) {
605 case MESH_OP_JOB: return grind(ctx, arg);
606 case MESH_OP_REQ: {
607 uint32_t n = request(ctx);
608 mesh_ctx_respond(ctx, n);
609 return n;
610 }
611 case OP_ZBITS:
612 case OP_RESET:
613 case OP_NBITS:
614 case OP_GENESIS:
615 case OP_CHUNK:
616 case OP_BASE:
617 case OP_TIME: {
618 mesh_view v = mesh_ctx_view(ctx);
619 uint32_t c;
620 if (chunk_log2(ctx, &c)) sync_chunk(v, c);
621 bool ok = false;
622 uint32_t g;
623 switch (op) {
624 case OP_ZBITS:
625 if (arg >= 1 && arg <= 255) {
626 g = config_begin(v);
627 set_zbits(v, arg);
628 config_end(v, g);
629 ok = true;
630 }
631 break;
632 case OP_RESET:
633 g = config_begin(v);
634 reset_stats(v);
635 config_end(v, g);
636 ok = true;
637 break;
638 case OP_NBITS:
639 g = config_begin(v);
640 ok = set_nbits(v, arg);
641 config_end(v, g);
642 break;
643 case OP_GENESIS:
644 if (arg <= 255) {
645 g = config_begin(v);
646 set_genesis(v);
647 if (arg == 0) ok = set_nbits(v, mesh_view_u32(v, V_NBITS));
648 else {
649 set_zbits(v, arg);
650 ok = true;
651 }
652 config_end(v, g);
653 }
654 break;
655 case OP_BASE:
656 g = config_begin(v);
657 mesh_view_set_u32(v, V_BASE, arg);
658 restart(v);
659 config_end(v, g);
660 ok = true;
661 break;
662 case OP_TIME:
663 g = config_begin(v);
664 mesh_view_set_u32(v, V_HEADER + 68, arg);
665 header_changed(v);
666 config_end(v, g);
667 ok = true;
668 break;
669 default: break;
670 }
671 return ok ? MESH_R_NONE : MESH_R_BAD_OP;
672 }
673 default: return MESH_R_BAD_OP;
674 }
675}
676
677MESH_PROGRAM(init, task)