zeldhash-miner 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -95,7 +95,7 @@ Creates a new miner instance.
95
95
  interface MineParams {
96
96
  inputs: TxInput[]; // UTXOs to spend
97
97
  outputs: TxOutput[]; // Destinations (one must be change: true)
98
- targetZeros: number; // Leading zero hex digits (1–32)
98
+ targetZeros: number; // Leading zero hex digits (0–32)
99
99
  startNonce?: bigint; // Starting point (default 0n)
100
100
  batchSize?: number; // Override instance batch size
101
101
  distribution?: bigint[]; // Optional ZELD distribution values
@@ -207,34 +207,43 @@ Notes:
207
207
 
208
208
  ## Runtime Notes
209
209
 
210
- ### WASM Asset Loading
210
+ ### Asset Setup
211
211
 
212
- The SDK automatically loads WASM assets from `/wasm/` on your application's origin. This works out of the box with most setups:
212
+ The SDK requires WASM and worker files to be served from your application's public folder. All assets are bundled in a single `assets/` folder for easy setup:
213
213
 
214
- 1. **Copy assets to your public folder**: Copy the `wasm/` folder from `node_modules/zeldhash-miner/` to your app's `public/` directory:
215
- ```bash
216
- cp -r node_modules/zeldhash-miner/wasm public/wasm
217
- ```
214
+ ```bash
215
+ # Copy all required assets to your public folder
216
+ cp -r node_modules/zeldhash-miner/assets public/zeldhash-miner
217
+ ```
218
+
219
+ Or add a postinstall script to your `package.json`:
220
+ ```json
221
+ {
222
+ "scripts": {
223
+ "postinstall": "cp -r node_modules/zeldhash-miner/assets public/zeldhash-miner"
224
+ }
225
+ }
226
+ ```
218
227
 
219
- 2. **Also copy the worker**: The worker script needs to be served from your app as well:
220
- ```bash
221
- cp node_modules/zeldhash-miner/dist/worker.js public/worker.js
222
- ```
228
+ This copies 3 files to `public/zeldhash-miner/`:
229
+ - `zeldhash_miner_wasm.js` - WASM JavaScript bindings
230
+ - `zeldhash_miner_wasm_bg.wasm` - WASM binary
231
+ - `worker.js` - Web Worker for mining
223
232
 
224
- The SDK bootstraps `globalThis.__ZELDMINER_WASM_BASE__` to `/wasm/` (relative to your app's origin) automatically, so WASM files are fetched from `https://your-app.com/wasm/` rather than from inside `node_modules/`.
233
+ The SDK automatically loads assets from `/zeldhash-miner/` on your application's origin.
225
234
 
226
- ### Custom WASM Path
235
+ ### Custom Asset Path
227
236
 
228
- If you need to serve WASM assets from a different location, you can override the base path:
237
+ If you need to serve assets from a different location, override the base path before importing:
229
238
 
230
239
  ```ts
231
240
  // Set before importing ZeldMiner
232
- globalThis.__ZELDMINER_WASM_BASE__ = "/custom/path/to/wasm/";
241
+ globalThis.__ZELDMINER_WASM_BASE__ = "/custom/path/to/assets/";
233
242
  ```
234
243
 
235
244
  Or use environment variables with Vite:
236
245
  ```bash
237
- VITE_ZELDMINER_WASM_BASE=/custom/wasm/
246
+ VITE_ZELDMINER_WASM_BASE=/custom/assets/
238
247
  ```
239
248
 
240
249
  ### WebGPU
@@ -0,0 +1,354 @@
1
+ var u = /* @__PURE__ */ ((t) => (t.INVALID_ADDRESS = "INVALID_ADDRESS", t.UNSUPPORTED_ADDRESS_TYPE = "UNSUPPORTED_ADDRESS_TYPE", t.INSUFFICIENT_FUNDS = "INSUFFICIENT_FUNDS", t.MULTIPLE_CHANGE_OUTPUTS = "MULTIPLE_CHANGE_OUTPUTS", t.INVALID_INPUT = "INVALID_INPUT", t.WEBGPU_NOT_AVAILABLE = "WEBGPU_NOT_AVAILABLE", t.WORKER_ERROR = "WORKER_ERROR", t.MINING_ABORTED = "MINING_ABORTED", t.DUST_OUTPUT = "DUST_OUTPUT", t))(u || {});
2
+ const N = (1n << 64n) - 1n, C = (t) => {
3
+ if (t < 0n)
4
+ throw new Error("nonce must be non-negative");
5
+ if (t === 0n) return 1;
6
+ let e = 0, n = t;
7
+ for (; n > 0n; )
8
+ e += 1, n >>= 8n;
9
+ return e;
10
+ }, X = (t) => {
11
+ if (t < 0n)
12
+ throw new Error("nonce must be non-negative");
13
+ if (t <= 23n) return 1;
14
+ if (t <= 0xffn) return 2;
15
+ if (t <= 0xffffn) return 3;
16
+ if (t <= 0xffffffffn) return 5;
17
+ if (t <= N) return 9;
18
+ throw new Error("nonce range exceeds u64");
19
+ }, j = (t) => {
20
+ if (!Number.isInteger(t) || t <= 0 || t > 8)
21
+ throw new Error("nonceLength must be between 1 and 8");
22
+ return (1n << BigInt(t * 8)) - 1n;
23
+ }, H = (t) => {
24
+ switch (t) {
25
+ case 1:
26
+ return 23n;
27
+ case 2:
28
+ return 0xffn;
29
+ case 3:
30
+ return 0xffffn;
31
+ case 5:
32
+ return 0xffffffffn;
33
+ case 9:
34
+ return N;
35
+ default:
36
+ throw new Error("cbor nonceLength must be one of 1, 2, 3, 5, 9");
37
+ }
38
+ }, Y = (t, e) => {
39
+ if (t < 0n)
40
+ throw new Error("startNonce must be non-negative");
41
+ if (!Number.isInteger(e) || e <= 0)
42
+ throw new Error("batchSize must be a positive integer");
43
+ const n = t + BigInt(e - 1);
44
+ if (n > N)
45
+ throw new Error("nonce range exceeds u64");
46
+ const r = [];
47
+ let i = t;
48
+ for (; i <= n; ) {
49
+ const s = C(i), o = j(s), a = n < o ? n : o, l = a - i + 1n;
50
+ if (l > BigInt(Number.MAX_SAFE_INTEGER))
51
+ throw new Error("segment size exceeds safe integer range");
52
+ if (r.push({
53
+ start: i,
54
+ size: Number(l),
55
+ nonceLength: s
56
+ }), a === n)
57
+ break;
58
+ i = a + 1n;
59
+ }
60
+ return r;
61
+ }, J = (t, e) => {
62
+ if (t < 0n)
63
+ throw new Error("startNonce must be non-negative");
64
+ if (!Number.isInteger(e) || e <= 0)
65
+ throw new Error("batchSize must be a positive integer");
66
+ const n = t + BigInt(e - 1);
67
+ if (n > N)
68
+ throw new Error("nonce range exceeds u64");
69
+ const r = [];
70
+ let i = t;
71
+ for (; i <= n; ) {
72
+ const s = X(i), o = H(s), a = n < o ? n : o, l = a - i + 1n;
73
+ if (l > BigInt(Number.MAX_SAFE_INTEGER))
74
+ throw new Error("segment size exceeds safe integer range");
75
+ if (r.push({
76
+ start: i,
77
+ size: Number(l),
78
+ nonceLength: s
79
+ }), a === n)
80
+ break;
81
+ i = a + 1n;
82
+ }
83
+ return r;
84
+ }, Q = {};
85
+ if (typeof globalThis.__ZELDMINER_WASM_BASE__ > "u")
86
+ try {
87
+ const t = typeof window < "u" && window.location?.origin ? window.location.origin : typeof self < "u" && self.location?.origin ? self.location.origin : "http://localhost";
88
+ globalThis.__ZELDMINER_WASM_BASE__ = new URL("/zeldhash-miner/", t).href;
89
+ } catch {
90
+ globalThis.__ZELDMINER_WASM_BASE__ = "/zeldhash-miner/";
91
+ }
92
+ let T = null, w = null, W = !1;
93
+ const Z = () => {
94
+ if (W) return;
95
+ W = !0;
96
+ const t = globalThis.GPUAdapter?.prototype, e = t?.requestDevice;
97
+ !t || typeof e != "function" || (t.requestDevice = function(r) {
98
+ if (r?.requiredLimits && typeof this.limits == "object") {
99
+ const i = r.requiredLimits, s = this.limits;
100
+ for (const o of Object.keys(i))
101
+ (!(o in s) || s[o] === void 0) && delete i[o];
102
+ }
103
+ return e.call(this, r);
104
+ });
105
+ }, B = (t) => t.endsWith("/") ? t : `${t}/`, A = (t) => {
106
+ const e = t.trim();
107
+ return e && (typeof window < "u" && typeof window.location?.origin == "string" ? B(new URL(e, window.location.origin).href) : B(new URL(e, import.meta.url).href));
108
+ }, tt = () => {
109
+ const t = globalThis.__ZELDMINER_WASM_BASE__;
110
+ if (typeof t == "string" && t.trim())
111
+ return A(t);
112
+ const e = Q?.VITE_ZELDMINER_WASM_BASE;
113
+ if (typeof e == "string" && e.trim())
114
+ return A(e);
115
+ const n = "/";
116
+ return n.trim() ? A(`${B(n.trim())}zeldhash-miner/`) : A("/zeldhash-miner/");
117
+ }, x = tt(), z = `${x}zeldhash_miner_wasm.js`, et = `${x}zeldhash_miner_wasm_bg.wasm`, nt = async (t) => (0, eval)("s => import(s)")(t), O = (t) => t instanceof Error ? t.message : String(t), rt = async () => {
118
+ Z();
119
+ let t;
120
+ try {
121
+ t = await nt(
122
+ /* @vite-ignore */
123
+ z
124
+ );
125
+ } catch (r) {
126
+ throw new Error(
127
+ `Failed to import WASM bundle (${z}). Did you run ./scripts/build-wasm.sh? (${O(r)})`
128
+ );
129
+ }
130
+ const e = t.default;
131
+ if (typeof e != "function")
132
+ throw new Error("WASM init function is missing from the bundle.");
133
+ try {
134
+ const r = new URL(et, import.meta.url);
135
+ await e({ module_or_path: r });
136
+ } catch (r) {
137
+ throw new Error(
138
+ `Failed to initialize WASM bundle: ${O(r)}`
139
+ );
140
+ }
141
+ const n = t;
142
+ try {
143
+ n.init_panic_hook?.();
144
+ } catch {
145
+ }
146
+ return n;
147
+ }, it = async () => T || (w || (w = rt().then((t) => (T = t, t)).catch((t) => {
148
+ throw w = null, t;
149
+ })), w);
150
+ if (typeof globalThis.__ZELDMINER_WASM_BASE__ > "u")
151
+ try {
152
+ const t = typeof self < "u" && self.location?.origin ? self.location.origin : "http://localhost";
153
+ globalThis.__ZELDMINER_WASM_BASE__ = new URL("/zeldhash-miner/", t).href;
154
+ } catch {
155
+ globalThis.__ZELDMINER_WASM_BASE__ = "/zeldhash-miner/";
156
+ }
157
+ const U = self, ot = U.name ?? void 0, I = (t) => t instanceof Error ? t.message : String(t);
158
+ let R = "cpu", b = null;
159
+ const d = (t) => {
160
+ U.postMessage({ ...t, workerId: ot });
161
+ }, h = (t, e = u.WORKER_ERROR, n) => {
162
+ d({ type: "error", message: t, code: e, details: n });
163
+ }, st = (t) => typeof t == "object" && t !== null && "ok" in t, at = (t) => typeof t == "object" && t !== null && "nonce" in t && "txid" in t, ct = (t) => {
164
+ const e = BigInt(Number.MAX_SAFE_INTEGER);
165
+ return t > e ? Number.MAX_SAFE_INTEGER : t < -e ? -Number.MAX_SAFE_INTEGER : Number(t);
166
+ }, ut = (t) => ({
167
+ nonceLength: t.nonceLength,
168
+ prefix: new Uint8Array(t.prefix),
169
+ suffix: new Uint8Array(t.suffix),
170
+ useCborNonce: t.useCborNonce
171
+ }), F = (t, e) => {
172
+ t.set(e.nonceLength, ut(e));
173
+ }, lt = async (t, e, n, r, i) => {
174
+ const s = t.get(r.nonceLength);
175
+ if (s)
176
+ return s;
177
+ const o = e.build_mining_template(
178
+ n.inputs,
179
+ n.outputs,
180
+ i,
181
+ BigInt(n.satsPerVbyte),
182
+ r.start,
183
+ r.size,
184
+ n.distribution ?? null
185
+ ), a = {
186
+ ...o,
187
+ nonceLength: r.nonceLength,
188
+ useCborNonce: o.useCborNonce ?? n.useCborNonce
189
+ };
190
+ return F(t, a), a;
191
+ }, ft = async (t, e, n, r, i, s, o, a) => {
192
+ if (e === "gpu") {
193
+ if (!t.mine_batch_gpu)
194
+ throw new Error("GPU mining requested but mine_batch_gpu is unavailable");
195
+ return t.mine_batch_gpu(
196
+ n,
197
+ r,
198
+ i,
199
+ s,
200
+ o,
201
+ a
202
+ );
203
+ }
204
+ return t.mine_batch_wasm(
205
+ n,
206
+ r,
207
+ i,
208
+ s,
209
+ o,
210
+ a
211
+ );
212
+ }, _t = async (t, e) => {
213
+ let n;
214
+ try {
215
+ if (n = await it(), R === "gpu") {
216
+ if (!n.mine_batch_gpu) {
217
+ h(
218
+ "GPU mining requested but mine_batch_gpu is unavailable",
219
+ u.WEBGPU_NOT_AVAILABLE
220
+ );
221
+ return;
222
+ }
223
+ n.init_gpu && await n.init_gpu();
224
+ }
225
+ } catch (g) {
226
+ const m = I(g), E = R === "gpu" ? u.WEBGPU_NOT_AVAILABLE : u.WORKER_ERROR;
227
+ h(`Failed to initialize WASM: ${m}`, E);
228
+ return;
229
+ }
230
+ const r = /* @__PURE__ */ new Map(), i = t.template.useCborNonce ?? !!(t.distribution && t.distribution.length > 0);
231
+ F(r, { ...t.template, useCborNonce: i });
232
+ const s = t.nonceStep ?? BigInt(t.batchSize), o = t.network === "signet" ? "testnet" : t.network;
233
+ let a = t.startNonce, l = 0n;
234
+ const G = performance.now();
235
+ for (; !e.signal.aborted; ) {
236
+ const g = a;
237
+ let m = t.batchSize, E = 0n;
238
+ for (; m > 0 && !e.signal.aborted; ) {
239
+ const D = g + E;
240
+ let c;
241
+ try {
242
+ c = (i ? J(D, m) : Y(D, m))[0];
243
+ } catch (_) {
244
+ h(
245
+ `Invalid nonce range: ${I(_)}`,
246
+ u.INVALID_INPUT
247
+ ), e.abort();
248
+ return;
249
+ }
250
+ let S;
251
+ try {
252
+ S = await lt(
253
+ r,
254
+ n,
255
+ {
256
+ inputs: t.inputs,
257
+ outputs: t.outputs,
258
+ satsPerVbyte: t.satsPerVbyte,
259
+ distribution: t.distribution,
260
+ useCborNonce: i
261
+ },
262
+ c,
263
+ o
264
+ );
265
+ } catch (_) {
266
+ h(
267
+ `Failed to build mining template: ${I(_)}`,
268
+ u.WORKER_ERROR
269
+ ), e.abort();
270
+ return;
271
+ }
272
+ let f;
273
+ const k = performance.now();
274
+ try {
275
+ f = await ft(
276
+ n,
277
+ R,
278
+ S.prefix,
279
+ S.suffix,
280
+ c.start,
281
+ c.size,
282
+ t.targetZeros,
283
+ i
284
+ );
285
+ } catch (_) {
286
+ const p = I(_);
287
+ h(
288
+ `Batch mining failed: ${p}`,
289
+ u.WORKER_ERROR
290
+ ), e.abort();
291
+ return;
292
+ }
293
+ const M = performance.now() - k;
294
+ if (st(f)) {
295
+ if (!f.ok) {
296
+ h(
297
+ f.error ?? "Validation failed",
298
+ u.INVALID_INPUT
299
+ ), e.abort();
300
+ return;
301
+ }
302
+ } else if (at(f)) {
303
+ const _ = l + E, p = BigInt(f.nonce) - c.start + 1n, L = _ + p, y = performance.now() - G, P = y > 0 ? ct(L) / (y / 1e3) : 0, q = c.start + p - 1n, K = {
304
+ psbt: "",
305
+ txid: f.txid,
306
+ nonce: BigInt(f.nonce),
307
+ attempts: L,
308
+ duration: y,
309
+ hashRate: P
310
+ };
311
+ d({
312
+ type: "found",
313
+ result: K,
314
+ hashesProcessed: L,
315
+ hashRate: P,
316
+ lastNonce: q
317
+ }), e.abort();
318
+ return;
319
+ }
320
+ l += BigInt(c.size), E += BigInt(c.size), m -= c.size;
321
+ const v = M > 0 ? c.size / (M / 1e3) : c.size, $ = c.start + BigInt(c.size) - 1n;
322
+ d({ type: "progress", hashesProcessed: l, hashRate: v, lastNonce: $ });
323
+ }
324
+ if (e.signal.aborted)
325
+ break;
326
+ const V = g + BigInt(t.batchSize) - 1n;
327
+ d({ type: "batch_complete", lastNonce: V }), a = g + s;
328
+ }
329
+ }, ht = (t) => {
330
+ const e = new AbortController();
331
+ b?.abort(), b = e, _t(t, e).finally(() => {
332
+ b === e && (b = null);
333
+ });
334
+ };
335
+ U.addEventListener("message", (t) => {
336
+ const e = t.data;
337
+ switch (e.type) {
338
+ case "init":
339
+ R = e.mode, d({ type: "ready" });
340
+ break;
341
+ case "mine":
342
+ ht(e);
343
+ break;
344
+ case "stop":
345
+ b?.abort();
346
+ break;
347
+ default:
348
+ h(
349
+ `Unknown message type: ${e.type}`,
350
+ u.WORKER_ERROR
351
+ );
352
+ }
353
+ });
354
+ //# sourceMappingURL=worker.js.map