skip to content
==> 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)