zeldhash-miner 0.1.0
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/LICENSE +22 -0
- package/README.md +244 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +775 -0
- package/dist/index.js.map +1 -0
- package/dist/worker.js +339 -0
- package/dist/worker.js.map +1 -0
- package/package.json +58 -0
- package/wasm/package.json +31 -0
- package/wasm/zeldhash_miner_wasm.d.ts +82 -0
- package/wasm/zeldhash_miner_wasm.js +1412 -0
- package/wasm/zeldhash_miner_wasm_bg.wasm +0 -0
- package/wasm/zeldhash_miner_wasm_bg.wasm.d.ts +27 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
var o = /* @__PURE__ */ ((s) => (s.INVALID_ADDRESS = "INVALID_ADDRESS", s.UNSUPPORTED_ADDRESS_TYPE = "UNSUPPORTED_ADDRESS_TYPE", s.INSUFFICIENT_FUNDS = "INSUFFICIENT_FUNDS", s.NO_CHANGE_OUTPUT = "NO_CHANGE_OUTPUT", s.MULTIPLE_CHANGE_OUTPUTS = "MULTIPLE_CHANGE_OUTPUTS", s.INVALID_INPUT = "INVALID_INPUT", s.WEBGPU_NOT_AVAILABLE = "WEBGPU_NOT_AVAILABLE", s.WORKER_ERROR = "WORKER_ERROR", s.MINING_ABORTED = "MINING_ABORTED", s.DUST_OUTPUT = "DUST_OUTPUT", s))(o || {});
|
|
2
|
+
const $ = (s) => s instanceof Error ? s.message : String(s);
|
|
3
|
+
class _ extends Error {
|
|
4
|
+
code;
|
|
5
|
+
details;
|
|
6
|
+
constructor(t, e, r) {
|
|
7
|
+
super(e), this.name = "ZeldMinerError", this.code = t, this.details = r;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const M = (s, t = o.WORKER_ERROR, e) => {
|
|
11
|
+
if (s instanceof _)
|
|
12
|
+
return s;
|
|
13
|
+
const r = $(s);
|
|
14
|
+
return new _(t, r, e);
|
|
15
|
+
}, u = (s, t, e) => new _(s, t, e), k = (s) => {
|
|
16
|
+
const t = BigInt(Number.MAX_SAFE_INTEGER);
|
|
17
|
+
return s > t ? Number.MAX_SAFE_INTEGER : s < -t ? -Number.MAX_SAFE_INTEGER : Number(s);
|
|
18
|
+
};
|
|
19
|
+
class C {
|
|
20
|
+
mode;
|
|
21
|
+
batchSize;
|
|
22
|
+
workerCount;
|
|
23
|
+
listeners;
|
|
24
|
+
workers = [];
|
|
25
|
+
readyPromise;
|
|
26
|
+
abortHandler;
|
|
27
|
+
stride;
|
|
28
|
+
cleanupExternalAbort = () => {
|
|
29
|
+
this.externalAbort && (this.externalAbort.removeEventListener("abort", this.abortHandler), this.externalAbort = void 0);
|
|
30
|
+
};
|
|
31
|
+
running = !1;
|
|
32
|
+
paused = !1;
|
|
33
|
+
startedAt = null;
|
|
34
|
+
txInputs;
|
|
35
|
+
txOutputs;
|
|
36
|
+
txNetwork;
|
|
37
|
+
satsPerVbyte;
|
|
38
|
+
template;
|
|
39
|
+
targetZeros;
|
|
40
|
+
startNonce = 0n;
|
|
41
|
+
txDistribution;
|
|
42
|
+
externalAbort;
|
|
43
|
+
terminated = !1;
|
|
44
|
+
constructor(t) {
|
|
45
|
+
this.mode = t.mode, this.batchSize = t.batchSize, this.workerCount = this.mode === "gpu" ? 1 : Math.max(1, t.workerThreads), this.stride = this.mode === "gpu" ? BigInt(this.batchSize) : BigInt(this.batchSize) * BigInt(this.workerCount), this.listeners = {
|
|
46
|
+
ready: /* @__PURE__ */ new Set(),
|
|
47
|
+
progress: /* @__PURE__ */ new Set(),
|
|
48
|
+
found: /* @__PURE__ */ new Set(),
|
|
49
|
+
error: /* @__PURE__ */ new Set(),
|
|
50
|
+
stopped: /* @__PURE__ */ new Set()
|
|
51
|
+
}, this.abortHandler = () => this.stop(), this.readyPromise = this.spawnWorkers();
|
|
52
|
+
}
|
|
53
|
+
on(t, e) {
|
|
54
|
+
this.listeners[t].add(e);
|
|
55
|
+
}
|
|
56
|
+
off(t, e) {
|
|
57
|
+
this.listeners[t].delete(e);
|
|
58
|
+
}
|
|
59
|
+
emit(t, e) {
|
|
60
|
+
this.listeners[t].forEach((r) => r(e));
|
|
61
|
+
}
|
|
62
|
+
async spawnWorkers() {
|
|
63
|
+
const t = [];
|
|
64
|
+
for (let e = 0; e < this.workerCount; e += 1) {
|
|
65
|
+
const r = new Worker(new URL(
|
|
66
|
+
/* @vite-ignore */
|
|
67
|
+
"/worker.js",
|
|
68
|
+
import.meta.url
|
|
69
|
+
), {
|
|
70
|
+
type: "module",
|
|
71
|
+
/* @vite-ignore */
|
|
72
|
+
name: `zeldminer-worker-${e}`
|
|
73
|
+
}), n = {
|
|
74
|
+
worker: r,
|
|
75
|
+
hashesProcessed: 0n,
|
|
76
|
+
hashRate: 0,
|
|
77
|
+
nextNonce: 0n,
|
|
78
|
+
processedBase: 0n
|
|
79
|
+
};
|
|
80
|
+
this.workers.push(n), t.push(
|
|
81
|
+
new Promise((a) => {
|
|
82
|
+
const h = (c) => {
|
|
83
|
+
c.data.type === "ready" && (r.removeEventListener("message", h), a());
|
|
84
|
+
};
|
|
85
|
+
r.addEventListener("message", h);
|
|
86
|
+
})
|
|
87
|
+
), r.addEventListener(
|
|
88
|
+
"message",
|
|
89
|
+
(a) => this.handleWorkerMessage(n, a.data)
|
|
90
|
+
);
|
|
91
|
+
const i = { type: "init", mode: this.mode };
|
|
92
|
+
r.postMessage(i);
|
|
93
|
+
}
|
|
94
|
+
await Promise.all(t), this.emit("ready", void 0);
|
|
95
|
+
}
|
|
96
|
+
handleWorkerMessage(t, e) {
|
|
97
|
+
switch (e.type) {
|
|
98
|
+
case "ready":
|
|
99
|
+
break;
|
|
100
|
+
case "progress":
|
|
101
|
+
t.hashesProcessed = t.processedBase + e.hashesProcessed, t.hashRate = e.hashRate, t.lastNonce = e.lastNonce ?? t.lastNonce, this.emitProgress();
|
|
102
|
+
break;
|
|
103
|
+
case "batch_complete":
|
|
104
|
+
t.lastNonce = e.lastNonce, t.nextNonce = this.computeNextNonce(e.lastNonce);
|
|
105
|
+
break;
|
|
106
|
+
case "found":
|
|
107
|
+
t.hashesProcessed = t.processedBase + (e.hashesProcessed ?? t.hashesProcessed), t.hashRate = e.hashRate ?? t.hashRate, t.lastNonce = e.lastNonce ?? t.lastNonce;
|
|
108
|
+
const r = this.workers.reduce(
|
|
109
|
+
(h, c) => h + c.hashesProcessed,
|
|
110
|
+
0n
|
|
111
|
+
), n = this.startedAt ? performance.now() - this.startedAt : 0, i = n > 0 ? k(r) / (n / 1e3) : 0, a = {
|
|
112
|
+
...e.result,
|
|
113
|
+
attempts: r,
|
|
114
|
+
duration: n,
|
|
115
|
+
hashRate: i
|
|
116
|
+
};
|
|
117
|
+
this.running = !1, this.paused = !1, this.terminateWorkers(), this.cleanupExternalAbort(), this.emit("found", a);
|
|
118
|
+
break;
|
|
119
|
+
case "error":
|
|
120
|
+
this.running = !1, this.paused = !1, this.terminateWorkers(), this.cleanupExternalAbort(), this.emit(
|
|
121
|
+
"error",
|
|
122
|
+
new _(
|
|
123
|
+
e.code ?? o.WORKER_ERROR,
|
|
124
|
+
e.message,
|
|
125
|
+
{
|
|
126
|
+
workerId: e.workerId,
|
|
127
|
+
...e.details ?? {}
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
emitProgress() {
|
|
135
|
+
if (!this.running) return;
|
|
136
|
+
const t = this.workers.reduce(
|
|
137
|
+
(i, a) => i + a.hashesProcessed,
|
|
138
|
+
0n
|
|
139
|
+
), e = this.startedAt ? performance.now() - this.startedAt : 0, r = e > 0 ? k(t) / (e / 1e3) : 0, n = {
|
|
140
|
+
hashesProcessed: t,
|
|
141
|
+
hashRate: r,
|
|
142
|
+
elapsedMs: e
|
|
143
|
+
};
|
|
144
|
+
this.emit("progress", n);
|
|
145
|
+
}
|
|
146
|
+
computeNextNonce(t) {
|
|
147
|
+
const e = this.stride - BigInt(this.batchSize);
|
|
148
|
+
return t + 1n + e;
|
|
149
|
+
}
|
|
150
|
+
stopWorkers() {
|
|
151
|
+
this.terminated || this.workers.forEach(
|
|
152
|
+
(t) => t.worker.postMessage({ type: "stop" })
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
terminateWorkers() {
|
|
156
|
+
this.terminated || (this.stopWorkers(), this.workers.forEach((t) => {
|
|
157
|
+
try {
|
|
158
|
+
t.worker.terminate(), t.terminated = !0;
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}), this.terminated = !0);
|
|
162
|
+
}
|
|
163
|
+
async start(t) {
|
|
164
|
+
if (await this.readyPromise, this.stopWorkers(), this.running = !0, this.paused = !1, this.startedAt = performance.now(), this.txInputs = t.inputs, this.txOutputs = t.outputs, this.txNetwork = t.network === "signet" ? "testnet" : t.network, this.satsPerVbyte = t.satsPerVbyte, this.template = t.template, this.targetZeros = t.targetZeros, this.startNonce = t.startNonce ?? 0n, this.txDistribution = t.distribution, this.externalAbort && this.externalAbort !== t.signal && this.cleanupExternalAbort(), t.signal && (this.externalAbort = t.signal, t.signal.addEventListener("abort", this.abortHandler, { once: !0 })), !this.template || !this.txInputs || !this.txOutputs || this.targetZeros === void 0 || this.satsPerVbyte === void 0 || !this.txNetwork)
|
|
165
|
+
throw new Error("Mining parameters are missing");
|
|
166
|
+
const e = this.txInputs, r = this.txOutputs, n = this.txNetwork, i = this.satsPerVbyte, a = this.template, h = this.targetZeros, c = this.txDistribution, p = this.stride;
|
|
167
|
+
this.workers.forEach((l, d) => {
|
|
168
|
+
l.processedBase = 0n, l.hashesProcessed = 0n, l.hashRate = 0, l.lastNonce = void 0;
|
|
169
|
+
const m = this.startNonce + BigInt(d) * BigInt(this.batchSize);
|
|
170
|
+
l.nextNonce = m;
|
|
171
|
+
const w = {
|
|
172
|
+
type: "mine",
|
|
173
|
+
inputs: e,
|
|
174
|
+
outputs: r,
|
|
175
|
+
network: n,
|
|
176
|
+
satsPerVbyte: i,
|
|
177
|
+
template: a,
|
|
178
|
+
startNonce: m,
|
|
179
|
+
batchSize: this.batchSize,
|
|
180
|
+
targetZeros: h,
|
|
181
|
+
nonceStep: p,
|
|
182
|
+
distribution: c
|
|
183
|
+
};
|
|
184
|
+
l.worker.postMessage(w);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
pause() {
|
|
188
|
+
this.running && (this.running = !1, this.paused = !0, this.stopWorkers());
|
|
189
|
+
}
|
|
190
|
+
async resume() {
|
|
191
|
+
if (!this.paused || !this.template || !this.txInputs || !this.txOutputs || this.targetZeros === void 0 || this.satsPerVbyte === void 0 || !this.txNetwork)
|
|
192
|
+
return;
|
|
193
|
+
await this.readyPromise, this.running = !0, this.paused = !1, this.startedAt || (this.startedAt = performance.now());
|
|
194
|
+
const t = this.txInputs, e = this.txOutputs, r = this.txNetwork, n = this.satsPerVbyte, i = this.template, a = this.targetZeros, h = this.txDistribution, c = this.stride;
|
|
195
|
+
this.workers.forEach((p, l) => {
|
|
196
|
+
p.processedBase = p.hashesProcessed, p.hashRate = 0;
|
|
197
|
+
const d = p.nextNonce || this.startNonce + BigInt(l) * BigInt(this.batchSize), m = {
|
|
198
|
+
type: "mine",
|
|
199
|
+
inputs: t,
|
|
200
|
+
outputs: e,
|
|
201
|
+
network: r,
|
|
202
|
+
satsPerVbyte: n,
|
|
203
|
+
template: i,
|
|
204
|
+
startNonce: d,
|
|
205
|
+
batchSize: this.batchSize,
|
|
206
|
+
targetZeros: a,
|
|
207
|
+
nonceStep: c,
|
|
208
|
+
distribution: h
|
|
209
|
+
};
|
|
210
|
+
p.worker.postMessage(m);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
stop() {
|
|
214
|
+
!this.running && !this.paused || (this.running = !1, this.paused = !1, this.terminateWorkers(), this.cleanupExternalAbort(), this.emit("stopped", void 0));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const q = {};
|
|
218
|
+
let P = null, N = null, D = !1;
|
|
219
|
+
const Z = () => {
|
|
220
|
+
if (D) return;
|
|
221
|
+
D = !0;
|
|
222
|
+
const s = globalThis.GPUAdapter?.prototype, t = s?.requestDevice;
|
|
223
|
+
!s || typeof t != "function" || (s.requestDevice = function(r) {
|
|
224
|
+
if (r?.requiredLimits && typeof this.limits == "object") {
|
|
225
|
+
const n = r.requiredLimits, i = this.limits;
|
|
226
|
+
for (const a of Object.keys(n))
|
|
227
|
+
(!(a in i) || i[a] === void 0) && delete n[a];
|
|
228
|
+
}
|
|
229
|
+
return t.call(this, r);
|
|
230
|
+
});
|
|
231
|
+
}, S = (s) => s.endsWith("/") ? s : `${s}/`, A = (s) => {
|
|
232
|
+
const t = s.trim();
|
|
233
|
+
return t && (typeof window < "u" && typeof window.location?.origin == "string" ? S(new URL(t, window.location.origin).href) : S(new URL(t, import.meta.url).href));
|
|
234
|
+
}, H = () => {
|
|
235
|
+
const s = globalThis.__ZELDMINER_WASM_BASE__;
|
|
236
|
+
if (typeof s == "string" && s.trim())
|
|
237
|
+
return A(s);
|
|
238
|
+
const t = q?.VITE_ZELDMINER_WASM_BASE;
|
|
239
|
+
if (typeof t == "string" && t.trim())
|
|
240
|
+
return A(t);
|
|
241
|
+
const e = "/";
|
|
242
|
+
return e.trim() ? A(`${S(e.trim())}wasm/`) : A("/wasm/");
|
|
243
|
+
}, W = H(), L = `${W}zeldhash_miner_wasm.js`, X = `${W}zeldhash_miner_wasm_bg.wasm`, v = (s) => s instanceof Error ? s.message : String(s), K = async () => {
|
|
244
|
+
Z();
|
|
245
|
+
let s;
|
|
246
|
+
try {
|
|
247
|
+
s = await import(
|
|
248
|
+
/* @vite-ignore */
|
|
249
|
+
L
|
|
250
|
+
);
|
|
251
|
+
} catch (r) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`Failed to import WASM bundle (${L}). Did you run ./scripts/build-wasm.sh? (${v(r)})`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
const t = s.default;
|
|
257
|
+
if (typeof t != "function")
|
|
258
|
+
throw new Error("WASM init function is missing from the bundle.");
|
|
259
|
+
try {
|
|
260
|
+
const r = new URL(X, import.meta.url);
|
|
261
|
+
await t(r);
|
|
262
|
+
} catch (r) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Failed to initialize WASM bundle: ${v(r)}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const e = s;
|
|
268
|
+
try {
|
|
269
|
+
e.init_panic_hook?.();
|
|
270
|
+
} catch {
|
|
271
|
+
}
|
|
272
|
+
return e;
|
|
273
|
+
}, j = async () => P || (N || (N = K().then((s) => (P = s, s)).catch((s) => {
|
|
274
|
+
throw N = null, s;
|
|
275
|
+
})), N), E = (1n << 64n) - 1n, z = (s) => {
|
|
276
|
+
if (s < 0n)
|
|
277
|
+
throw new Error("nonce must be non-negative");
|
|
278
|
+
if (s === 0n) return 1;
|
|
279
|
+
let t = 0, e = s;
|
|
280
|
+
for (; e > 0n; )
|
|
281
|
+
t += 1, e >>= 8n;
|
|
282
|
+
return t;
|
|
283
|
+
}, F = (s) => {
|
|
284
|
+
if (s < 0n)
|
|
285
|
+
throw new Error("nonce must be non-negative");
|
|
286
|
+
if (s <= 23n) return 1;
|
|
287
|
+
if (s <= 0xffn) return 2;
|
|
288
|
+
if (s <= 0xffffn) return 3;
|
|
289
|
+
if (s <= 0xffffffffn) return 5;
|
|
290
|
+
if (s <= E) return 9;
|
|
291
|
+
throw new Error("nonce range exceeds u64");
|
|
292
|
+
}, Y = (s) => {
|
|
293
|
+
if (!Number.isInteger(s) || s <= 0 || s > 8)
|
|
294
|
+
throw new Error("nonceLength must be between 1 and 8");
|
|
295
|
+
return (1n << BigInt(s * 8)) - 1n;
|
|
296
|
+
}, J = (s) => {
|
|
297
|
+
switch (s) {
|
|
298
|
+
case 1:
|
|
299
|
+
return 23n;
|
|
300
|
+
case 2:
|
|
301
|
+
return 0xffn;
|
|
302
|
+
case 3:
|
|
303
|
+
return 0xffffn;
|
|
304
|
+
case 5:
|
|
305
|
+
return 0xffffffffn;
|
|
306
|
+
case 9:
|
|
307
|
+
return E;
|
|
308
|
+
default:
|
|
309
|
+
throw new Error("cbor nonceLength must be one of 1, 2, 3, 5, 9");
|
|
310
|
+
}
|
|
311
|
+
}, Q = (s, t) => {
|
|
312
|
+
if (s < 0n)
|
|
313
|
+
throw new Error("startNonce must be non-negative");
|
|
314
|
+
if (!Number.isInteger(t) || t <= 0)
|
|
315
|
+
throw new Error("batchSize must be a positive integer");
|
|
316
|
+
const e = s + BigInt(t - 1);
|
|
317
|
+
if (e > E)
|
|
318
|
+
throw new Error("nonce range exceeds u64");
|
|
319
|
+
const r = [];
|
|
320
|
+
let n = s;
|
|
321
|
+
for (; n <= e; ) {
|
|
322
|
+
const i = z(n), a = Y(i), h = e < a ? e : a, c = h - n + 1n;
|
|
323
|
+
if (c > BigInt(Number.MAX_SAFE_INTEGER))
|
|
324
|
+
throw new Error("segment size exceeds safe integer range");
|
|
325
|
+
if (r.push({
|
|
326
|
+
start: n,
|
|
327
|
+
size: Number(c),
|
|
328
|
+
nonceLength: i
|
|
329
|
+
}), h === e)
|
|
330
|
+
break;
|
|
331
|
+
n = h + 1n;
|
|
332
|
+
}
|
|
333
|
+
return r;
|
|
334
|
+
}, tt = (s, t) => {
|
|
335
|
+
if (s < 0n)
|
|
336
|
+
throw new Error("startNonce must be non-negative");
|
|
337
|
+
if (!Number.isInteger(t) || t <= 0)
|
|
338
|
+
throw new Error("batchSize must be a positive integer");
|
|
339
|
+
const e = s + BigInt(t - 1);
|
|
340
|
+
if (e > E)
|
|
341
|
+
throw new Error("nonce range exceeds u64");
|
|
342
|
+
const r = [];
|
|
343
|
+
let n = s;
|
|
344
|
+
for (; n <= e; ) {
|
|
345
|
+
const i = F(n), a = J(i), h = e < a ? e : a, c = h - n + 1n;
|
|
346
|
+
if (c > BigInt(Number.MAX_SAFE_INTEGER))
|
|
347
|
+
throw new Error("segment size exceeds safe integer range");
|
|
348
|
+
if (r.push({
|
|
349
|
+
start: n,
|
|
350
|
+
size: Number(c),
|
|
351
|
+
nonceLength: i
|
|
352
|
+
}), h === e)
|
|
353
|
+
break;
|
|
354
|
+
n = h + 1n;
|
|
355
|
+
}
|
|
356
|
+
return r;
|
|
357
|
+
}, et = /^[0-9a-fA-F]{64}$/, st = /^[0-9a-fA-F]+$/, T = (1n << 64n) - 1n, rt = 4294967295, nt = (s) => s === "p2tr" ? 330 : s === "p2wpkh" ? 310 : 546, it = (s) => s instanceof Error ? s.message : String(s), x = (s, t) => t.some((e) => s.includes(e)), O = (s, t, e) => {
|
|
358
|
+
const r = it(s), n = r.toLowerCase();
|
|
359
|
+
throw x(n, ["insufficient funds", "insufficient_funds"]) ? u(
|
|
360
|
+
o.INSUFFICIENT_FUNDS,
|
|
361
|
+
"Insufficient funds for outputs and fee",
|
|
362
|
+
{ ...e, cause: r }
|
|
363
|
+
) : x(n, ["change would be dust", "output amount below dust limit", "dust"]) ? u(
|
|
364
|
+
o.DUST_OUTPUT,
|
|
365
|
+
"Change would be dust",
|
|
366
|
+
{ ...e, cause: r }
|
|
367
|
+
) : M(s, o.WORKER_ERROR, {
|
|
368
|
+
...e,
|
|
369
|
+
context: t
|
|
370
|
+
});
|
|
371
|
+
}, y = (s) => s === "signet" ? "testnet" : s;
|
|
372
|
+
class ot {
|
|
373
|
+
network;
|
|
374
|
+
satsPerVbyte;
|
|
375
|
+
constructor(t, e) {
|
|
376
|
+
if (!Number.isFinite(e) || e <= 0)
|
|
377
|
+
throw u(
|
|
378
|
+
o.INVALID_INPUT,
|
|
379
|
+
"satsPerVbyte must be a positive number",
|
|
380
|
+
{ field: "satsPerVbyte" }
|
|
381
|
+
);
|
|
382
|
+
this.network = t, this.satsPerVbyte = e;
|
|
383
|
+
}
|
|
384
|
+
async getWasm() {
|
|
385
|
+
return j();
|
|
386
|
+
}
|
|
387
|
+
assertNonceRange(t, e, r) {
|
|
388
|
+
if (t < 0n || t > T)
|
|
389
|
+
throw u(
|
|
390
|
+
o.INVALID_INPUT,
|
|
391
|
+
"startNonce must be between 0 and 2^64 - 1",
|
|
392
|
+
{ startNonce: t }
|
|
393
|
+
);
|
|
394
|
+
if (!Number.isInteger(e) || e <= 0 || e > rt)
|
|
395
|
+
throw u(
|
|
396
|
+
o.INVALID_INPUT,
|
|
397
|
+
"batchSize must be a positive 32-bit integer",
|
|
398
|
+
{ batchSize: e }
|
|
399
|
+
);
|
|
400
|
+
const n = t + BigInt(e - 1);
|
|
401
|
+
if (n > T)
|
|
402
|
+
throw u(
|
|
403
|
+
o.INVALID_INPUT,
|
|
404
|
+
"nonce range exceeds u64",
|
|
405
|
+
{ startNonce: t, batchSize: e }
|
|
406
|
+
);
|
|
407
|
+
const i = r ? F : z, a = i(t), h = i(n);
|
|
408
|
+
if (a !== h) {
|
|
409
|
+
const c = r ? "CBOR length" : "byte-length";
|
|
410
|
+
throw u(
|
|
411
|
+
o.INVALID_INPUT,
|
|
412
|
+
`nonce range crosses ${c} boundary; reduce batch size`,
|
|
413
|
+
{ startNonce: t, batchSize: e }
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return a;
|
|
417
|
+
}
|
|
418
|
+
cloneInputs(t) {
|
|
419
|
+
return t.map((e) => ({ ...e }));
|
|
420
|
+
}
|
|
421
|
+
cloneOutputs(t) {
|
|
422
|
+
return t.map((e) => ({ ...e }));
|
|
423
|
+
}
|
|
424
|
+
validateInputs(t) {
|
|
425
|
+
if (!Array.isArray(t) || t.length === 0)
|
|
426
|
+
throw u(
|
|
427
|
+
o.INVALID_INPUT,
|
|
428
|
+
"At least one input is required"
|
|
429
|
+
);
|
|
430
|
+
t.forEach((e, r) => {
|
|
431
|
+
if (!et.test(e.txid))
|
|
432
|
+
throw u(
|
|
433
|
+
o.INVALID_INPUT,
|
|
434
|
+
`inputs[${r}].txid must be a 64-character hex`,
|
|
435
|
+
{ index: r }
|
|
436
|
+
);
|
|
437
|
+
if (!Number.isInteger(e.vout) || e.vout < 0)
|
|
438
|
+
throw u(
|
|
439
|
+
o.INVALID_INPUT,
|
|
440
|
+
`inputs[${r}].vout must be a non-negative integer`,
|
|
441
|
+
{ index: r }
|
|
442
|
+
);
|
|
443
|
+
if (!Number.isInteger(e.amount) || e.amount <= 0)
|
|
444
|
+
throw u(
|
|
445
|
+
o.INVALID_INPUT,
|
|
446
|
+
`inputs[${r}].amount must be a positive integer`,
|
|
447
|
+
{ index: r }
|
|
448
|
+
);
|
|
449
|
+
if (typeof e.scriptPubKey != "string" || e.scriptPubKey.length === 0 || e.scriptPubKey.length % 2 !== 0 || !st.test(e.scriptPubKey))
|
|
450
|
+
throw u(
|
|
451
|
+
o.INVALID_INPUT,
|
|
452
|
+
`inputs[${r}].scriptPubKey must be valid hex`,
|
|
453
|
+
{ index: r }
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
validateAddressResult(t, e, r) {
|
|
458
|
+
if (!t.ok) {
|
|
459
|
+
const n = t.error ?? "invalid address", a = n.toLowerCase().includes("unsupported") ? o.UNSUPPORTED_ADDRESS_TYPE : o.INVALID_ADDRESS;
|
|
460
|
+
throw u(
|
|
461
|
+
a,
|
|
462
|
+
`outputs[${e}].address is invalid (${n})`,
|
|
463
|
+
{ index: e }
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
if (t.network && t.network !== r && !(t.network === "testnet" && r === "signet"))
|
|
467
|
+
throw u(
|
|
468
|
+
o.INVALID_ADDRESS,
|
|
469
|
+
`outputs[${e}].address network mismatch`,
|
|
470
|
+
{ index: e }
|
|
471
|
+
);
|
|
472
|
+
if (t.addressType && t.addressType !== "p2tr" && t.addressType !== "p2wpkh")
|
|
473
|
+
throw u(
|
|
474
|
+
o.UNSUPPORTED_ADDRESS_TYPE,
|
|
475
|
+
`outputs[${e}].address uses an unsupported type`,
|
|
476
|
+
{ index: e, addressType: t.addressType }
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
async validateOutputs(t) {
|
|
480
|
+
if (!Array.isArray(t) || t.length === 0)
|
|
481
|
+
throw u(
|
|
482
|
+
o.INVALID_INPUT,
|
|
483
|
+
"At least one output is required"
|
|
484
|
+
);
|
|
485
|
+
const e = t.filter((i) => i.change).length;
|
|
486
|
+
if (e !== 1) {
|
|
487
|
+
const i = e === 0 ? o.NO_CHANGE_OUTPUT : o.MULTIPLE_CHANGE_OUTPUTS;
|
|
488
|
+
throw u(
|
|
489
|
+
i,
|
|
490
|
+
"Exactly one change output is required",
|
|
491
|
+
{ changeCount: e }
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const r = await this.getWasm(), n = y(this.network);
|
|
495
|
+
t.forEach((i, a) => {
|
|
496
|
+
const h = r.validate_address(i.address, n);
|
|
497
|
+
this.validateAddressResult(h, a, n);
|
|
498
|
+
const c = nt(h.addressType);
|
|
499
|
+
if (i.change) {
|
|
500
|
+
if (i.amount !== void 0 && (!Number.isInteger(i.amount) || i.amount < 0))
|
|
501
|
+
throw u(
|
|
502
|
+
o.INVALID_INPUT,
|
|
503
|
+
`outputs[${a}].amount must be a non-negative integer when provided`,
|
|
504
|
+
{ index: a }
|
|
505
|
+
);
|
|
506
|
+
} else if (!Number.isInteger(i.amount) || i.amount < c)
|
|
507
|
+
throw u(
|
|
508
|
+
o.DUST_OUTPUT,
|
|
509
|
+
`outputs[${a}].amount must be at least ${c} sats`,
|
|
510
|
+
{ index: a, addressType: h.addressType }
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
validateDistribution(t, e) {
|
|
515
|
+
if (e !== void 0) {
|
|
516
|
+
if (!Array.isArray(e))
|
|
517
|
+
throw u(
|
|
518
|
+
o.INVALID_INPUT,
|
|
519
|
+
"distribution must be an array of bigint values"
|
|
520
|
+
);
|
|
521
|
+
if (e.length !== t.length)
|
|
522
|
+
throw u(
|
|
523
|
+
o.INVALID_INPUT,
|
|
524
|
+
"distribution length must match number of outputs",
|
|
525
|
+
{ expected: t.length, actual: e.length }
|
|
526
|
+
);
|
|
527
|
+
return e.map((r, n) => {
|
|
528
|
+
if (typeof r != "bigint")
|
|
529
|
+
throw u(
|
|
530
|
+
o.INVALID_INPUT,
|
|
531
|
+
"distribution values must be bigint",
|
|
532
|
+
{ index: n }
|
|
533
|
+
);
|
|
534
|
+
if (r < 0n)
|
|
535
|
+
throw u(
|
|
536
|
+
o.INVALID_INPUT,
|
|
537
|
+
"distribution values must be non-negative",
|
|
538
|
+
{ index: n }
|
|
539
|
+
);
|
|
540
|
+
return r;
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async buildMiningTemplate(t) {
|
|
545
|
+
const { inputs: e, outputs: r, startNonce: n, batchSize: i, distribution: a } = t;
|
|
546
|
+
this.validateInputs(e), await this.validateOutputs(r);
|
|
547
|
+
const h = this.validateDistribution(r, a), c = Array.isArray(h), p = this.assertNonceRange(n, i, c), l = await this.getWasm();
|
|
548
|
+
try {
|
|
549
|
+
const d = l.build_mining_template(
|
|
550
|
+
this.cloneInputs(e),
|
|
551
|
+
this.cloneOutputs(r),
|
|
552
|
+
y(this.network),
|
|
553
|
+
BigInt(this.satsPerVbyte),
|
|
554
|
+
n,
|
|
555
|
+
i,
|
|
556
|
+
h ?? null
|
|
557
|
+
);
|
|
558
|
+
if (!d || !(d.prefix instanceof Uint8Array) || !(d.suffix instanceof Uint8Array))
|
|
559
|
+
throw u(
|
|
560
|
+
o.WORKER_ERROR,
|
|
561
|
+
"WASM returned an invalid mining template"
|
|
562
|
+
);
|
|
563
|
+
const m = d.useCborNonce ?? c;
|
|
564
|
+
return { ...d, nonceLength: p, useCborNonce: m };
|
|
565
|
+
} catch (d) {
|
|
566
|
+
throw O(d, "build_mining_template", {
|
|
567
|
+
startNonce: n,
|
|
568
|
+
batchSize: i,
|
|
569
|
+
distribution: h
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async buildPsbt(t) {
|
|
574
|
+
const { inputs: e, outputs: r, nonce: n, distribution: i } = t;
|
|
575
|
+
this.validateInputs(e), await this.validateOutputs(r);
|
|
576
|
+
const a = this.validateDistribution(r, i);
|
|
577
|
+
if (n < 0n || n > T)
|
|
578
|
+
throw u(
|
|
579
|
+
o.INVALID_INPUT,
|
|
580
|
+
"nonce must be between 0 and 2^64 - 1",
|
|
581
|
+
{ nonce: n }
|
|
582
|
+
);
|
|
583
|
+
const h = await this.getWasm();
|
|
584
|
+
try {
|
|
585
|
+
return h.build_psbt(
|
|
586
|
+
this.cloneInputs(e),
|
|
587
|
+
this.cloneOutputs(r),
|
|
588
|
+
y(this.network),
|
|
589
|
+
BigInt(this.satsPerVbyte),
|
|
590
|
+
n,
|
|
591
|
+
a ?? null
|
|
592
|
+
);
|
|
593
|
+
} catch (c) {
|
|
594
|
+
throw O(c, "build_psbt", { nonce: n, distribution: a });
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const V = 1, B = 32;
|
|
599
|
+
class at {
|
|
600
|
+
options;
|
|
601
|
+
builder;
|
|
602
|
+
coordinator = null;
|
|
603
|
+
listeners;
|
|
604
|
+
state = "idle";
|
|
605
|
+
stopRequested = !1;
|
|
606
|
+
constructor(t) {
|
|
607
|
+
this.options = this.validateOptions(t), this.builder = new ot(t.network, t.satsPerVbyte), this.listeners = {
|
|
608
|
+
progress: /* @__PURE__ */ new Set(),
|
|
609
|
+
found: /* @__PURE__ */ new Set(),
|
|
610
|
+
error: /* @__PURE__ */ new Set(),
|
|
611
|
+
stopped: /* @__PURE__ */ new Set()
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
on(t, e) {
|
|
615
|
+
this.listeners[t].add(e);
|
|
616
|
+
}
|
|
617
|
+
off(t, e) {
|
|
618
|
+
this.listeners[t].delete(e);
|
|
619
|
+
}
|
|
620
|
+
emit(t, e) {
|
|
621
|
+
this.listeners[t].forEach((r) => r(e));
|
|
622
|
+
}
|
|
623
|
+
validateOptions(t) {
|
|
624
|
+
if (!Number.isInteger(t.batchSize) || t.batchSize <= 0)
|
|
625
|
+
throw u(
|
|
626
|
+
o.INVALID_INPUT,
|
|
627
|
+
"batchSize must be a positive integer",
|
|
628
|
+
{ field: "batchSize" }
|
|
629
|
+
);
|
|
630
|
+
if (!Number.isInteger(t.workerThreads) || t.workerThreads <= 0)
|
|
631
|
+
throw u(
|
|
632
|
+
o.INVALID_INPUT,
|
|
633
|
+
"workerThreads must be a positive integer",
|
|
634
|
+
{ field: "workerThreads" }
|
|
635
|
+
);
|
|
636
|
+
if (!Number.isFinite(t.satsPerVbyte) || t.satsPerVbyte <= 0)
|
|
637
|
+
throw u(
|
|
638
|
+
o.INVALID_INPUT,
|
|
639
|
+
"satsPerVbyte must be a positive number",
|
|
640
|
+
{ field: "satsPerVbyte" }
|
|
641
|
+
);
|
|
642
|
+
return t;
|
|
643
|
+
}
|
|
644
|
+
async selectBackend() {
|
|
645
|
+
if (!this.options.useWebGPU || typeof navigator > "u") return "cpu";
|
|
646
|
+
const t = navigator.gpu;
|
|
647
|
+
return (typeof t?.requestAdapter == "function" ? await t.requestAdapter() : null) ? "gpu" : "cpu";
|
|
648
|
+
}
|
|
649
|
+
clearCoordinatorHandlers(t, e) {
|
|
650
|
+
e.progress && t.off("progress", e.progress), e.found && t.off("found", e.found), e.error && t.off("error", e.error), e.stopped && t.off("stopped", e.stopped);
|
|
651
|
+
}
|
|
652
|
+
async mineTransaction(t) {
|
|
653
|
+
if (this.state === "running")
|
|
654
|
+
throw u(
|
|
655
|
+
o.INVALID_INPUT,
|
|
656
|
+
"Mining is already in progress"
|
|
657
|
+
);
|
|
658
|
+
if (!Number.isInteger(t.targetZeros) || t.targetZeros < V || t.targetZeros > B)
|
|
659
|
+
throw u(
|
|
660
|
+
o.INVALID_INPUT,
|
|
661
|
+
`targetZeros must be an integer between ${V} and ${B}`,
|
|
662
|
+
{ targetZeros: t.targetZeros }
|
|
663
|
+
);
|
|
664
|
+
const e = t.startNonce ?? 0n, r = t.batchSize ?? this.options.batchSize;
|
|
665
|
+
if (!Number.isInteger(r) || r <= 0)
|
|
666
|
+
throw u(
|
|
667
|
+
o.INVALID_INPUT,
|
|
668
|
+
"batchSize must be a positive integer",
|
|
669
|
+
{ batchSize: r }
|
|
670
|
+
);
|
|
671
|
+
if (t.signal?.aborted)
|
|
672
|
+
throw u(
|
|
673
|
+
o.MINING_ABORTED,
|
|
674
|
+
"Abort signal already triggered"
|
|
675
|
+
);
|
|
676
|
+
const n = t.distribution, i = !!(n && n.length > 0);
|
|
677
|
+
let a = r;
|
|
678
|
+
try {
|
|
679
|
+
const [d] = i ? tt(e, r) : Q(e, r);
|
|
680
|
+
if (!d)
|
|
681
|
+
throw u(
|
|
682
|
+
o.INVALID_INPUT,
|
|
683
|
+
"Failed to compute nonce segments",
|
|
684
|
+
{ startNonce: e, batchSize: r }
|
|
685
|
+
);
|
|
686
|
+
a = d.size;
|
|
687
|
+
} catch (d) {
|
|
688
|
+
throw u(
|
|
689
|
+
o.INVALID_INPUT,
|
|
690
|
+
d instanceof Error ? d.message : String(d),
|
|
691
|
+
{ startNonce: e, batchSize: r }
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
const h = await this.selectBackend(), c = await this.builder.buildMiningTemplate({
|
|
695
|
+
inputs: t.inputs,
|
|
696
|
+
outputs: t.outputs,
|
|
697
|
+
startNonce: e,
|
|
698
|
+
batchSize: a,
|
|
699
|
+
distribution: n
|
|
700
|
+
}), p = this.options.network === "signet" ? "testnet" : this.options.network, l = new C({
|
|
701
|
+
mode: h,
|
|
702
|
+
batchSize: r,
|
|
703
|
+
workerThreads: this.options.workerThreads
|
|
704
|
+
});
|
|
705
|
+
return this.coordinator = l, this.state = "running", this.stopRequested = !1, new Promise((d, m) => {
|
|
706
|
+
let w = !1;
|
|
707
|
+
const R = () => {
|
|
708
|
+
this.clearCoordinatorHandlers(l, g), this.coordinator === l && (this.coordinator = null), this.state = "idle", this.stopRequested = !1;
|
|
709
|
+
}, G = (f) => {
|
|
710
|
+
w || (w = !0, R(), d(f));
|
|
711
|
+
}, I = (f) => {
|
|
712
|
+
if (w) return;
|
|
713
|
+
w = !0;
|
|
714
|
+
const b = M(f);
|
|
715
|
+
R(), this.emit("error", b), m(b);
|
|
716
|
+
}, g = {};
|
|
717
|
+
g.progress = (f) => {
|
|
718
|
+
this.emit("progress", f);
|
|
719
|
+
}, g.found = async (f) => {
|
|
720
|
+
try {
|
|
721
|
+
const b = await this.builder.buildPsbt({
|
|
722
|
+
inputs: t.inputs,
|
|
723
|
+
outputs: t.outputs,
|
|
724
|
+
nonce: f.nonce,
|
|
725
|
+
distribution: n
|
|
726
|
+
}), U = { ...f, psbt: b };
|
|
727
|
+
this.emit("found", U), G(U);
|
|
728
|
+
} catch (b) {
|
|
729
|
+
I(b);
|
|
730
|
+
}
|
|
731
|
+
}, g.error = (f) => {
|
|
732
|
+
I(f);
|
|
733
|
+
}, g.stopped = () => {
|
|
734
|
+
if (w) return;
|
|
735
|
+
const f = this.stopRequested ? u(
|
|
736
|
+
o.MINING_ABORTED,
|
|
737
|
+
"Mining stopped by caller"
|
|
738
|
+
) : u(
|
|
739
|
+
o.MINING_ABORTED,
|
|
740
|
+
"Mining halted"
|
|
741
|
+
);
|
|
742
|
+
this.emit("stopped", void 0), I(f);
|
|
743
|
+
}, l.on("progress", g.progress), l.on("found", g.found), l.on("error", g.error), l.on("stopped", g.stopped), l.start({
|
|
744
|
+
inputs: t.inputs,
|
|
745
|
+
outputs: t.outputs,
|
|
746
|
+
network: p,
|
|
747
|
+
satsPerVbyte: this.options.satsPerVbyte,
|
|
748
|
+
template: c,
|
|
749
|
+
targetZeros: t.targetZeros,
|
|
750
|
+
startNonce: e,
|
|
751
|
+
signal: t.signal,
|
|
752
|
+
distribution: n
|
|
753
|
+
}).catch(I);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
stop() {
|
|
757
|
+
this.coordinator && (this.stopRequested = !0, this.coordinator.stop());
|
|
758
|
+
}
|
|
759
|
+
pause() {
|
|
760
|
+
this.state !== "running" || !this.coordinator || (this.state = "paused", this.coordinator.pause());
|
|
761
|
+
}
|
|
762
|
+
async resume() {
|
|
763
|
+
this.state !== "paused" || !this.coordinator || (this.state = "running", await this.coordinator.resume());
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
export {
|
|
767
|
+
C as MiningCoordinator,
|
|
768
|
+
ot as TransactionBuilder,
|
|
769
|
+
at as ZeldMiner,
|
|
770
|
+
_ as ZeldMinerError,
|
|
771
|
+
o as ZeldMinerErrorCode,
|
|
772
|
+
u as createMinerError,
|
|
773
|
+
M as toZeldMinerError
|
|
774
|
+
};
|
|
775
|
+
//# sourceMappingURL=index.js.map
|