@totemsdk/txpow 1.0.2 → 1.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/dist/admission/challenge.js +14 -21
- package/dist/admission/commitment.js +9 -13
- package/dist/admission/index.js +6 -34
- package/dist/admission/mine.js +14 -17
- package/dist/admission/template.js +32 -47
- package/dist/admission/types.js +2 -5
- package/dist/admission/verify.js +12 -15
- package/dist/calibrate.js +4 -8
- package/dist/cjs/admission/challenge.d.ts +66 -0
- package/dist/cjs/admission/challenge.js +155 -0
- package/dist/cjs/admission/commitment.d.ts +34 -0
- package/dist/cjs/admission/commitment.js +73 -0
- package/dist/cjs/admission/index.d.ts +14 -0
- package/dist/cjs/admission/index.js +42 -0
- package/dist/cjs/admission/mine.d.ts +60 -0
- package/dist/cjs/admission/mine.js +146 -0
- package/dist/cjs/admission/template.d.ts +138 -0
- package/dist/cjs/admission/template.js +247 -0
- package/dist/cjs/admission/types.d.ts +224 -0
- package/dist/cjs/admission/types.js +24 -0
- package/dist/cjs/admission/verify.d.ts +50 -0
- package/dist/cjs/admission/verify.js +156 -0
- package/dist/cjs/calibrate.d.ts +35 -0
- package/dist/cjs/calibrate.js +68 -0
- package/dist/cjs/constants.d.ts +27 -0
- package/dist/cjs/constants.js +47 -0
- package/dist/cjs/difficulty.d.ts +44 -0
- package/dist/cjs/difficulty.js +111 -0
- package/dist/cjs/index.d.ts +28 -0
- package/dist/cjs/index.js +88 -0
- package/dist/cjs/magic.d.ts +19 -0
- package/dist/cjs/magic.js +35 -0
- package/dist/cjs/mine-wasm.d.ts +69 -0
- package/dist/cjs/mine-wasm.js +214 -0
- package/dist/cjs/mine.d.ts +131 -0
- package/dist/cjs/mine.js +407 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/serialization.d.ts +106 -0
- package/dist/cjs/serialization.js +160 -0
- package/dist/cjs/verify.d.ts +76 -0
- package/dist/cjs/verify.js +232 -0
- package/dist/cjs/wasm/miner.wasm +0 -0
- package/dist/cjs/workers/browser.worker.d.ts +26 -0
- package/dist/cjs/workers/browser.worker.js +63 -0
- package/dist/cjs/workers/node.worker.d.ts +33 -0
- package/dist/cjs/workers/node.worker.js +47 -0
- package/dist/constants.js +5 -8
- package/dist/difficulty.d.ts +24 -0
- package/dist/difficulty.js +54 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -62
- package/dist/magic.js +12 -15
- package/dist/mine-wasm.js +8 -15
- package/dist/mine.js +23 -31
- package/dist/serialization.js +28 -34
- package/dist/verify.js +15 -20
- package/dist/workers/browser.worker.js +4 -6
- package/dist/workers/node.worker.js +2 -3
- package/package.json +31 -13
package/dist/cjs/mine.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* mine.ts — Local TxPoW proof-of-work mining loop.
|
|
4
|
+
*
|
|
5
|
+
* Algorithm (from TxPoWChecker.java):
|
|
6
|
+
* txpowId = SHA3-256( serialize(TxHeader) )
|
|
7
|
+
* valid = txpowId < mTxnDifficulty (big-endian 256-bit comparison)
|
|
8
|
+
*
|
|
9
|
+
* Strategy:
|
|
10
|
+
* 1. The TxBody is fixed during mining — only mNonce in the header changes.
|
|
11
|
+
* 2. We precompute the "header tail" (everything after mNonce) once per mine.
|
|
12
|
+
* 3. Within each chunk (fixed nonce encoding length), we update only the nonce
|
|
13
|
+
* value bytes in place — no per-iteration allocation.
|
|
14
|
+
* 4. At each MiniNumber encoding boundary (nonce 128, 32768, 8388608, …) we
|
|
15
|
+
* re-allocate a new header buffer with the wider nonce field.
|
|
16
|
+
* 5. Between chunks we yield to the event loop so the calling thread is never blocked.
|
|
17
|
+
* 6. When the pre-compiled WASM binary is present (`dist/wasm/miner.wasm`), the
|
|
18
|
+
* inner loop runs in WASM for ~5-10x throughput. Falls back to pure JS otherwise.
|
|
19
|
+
* 7. In Node.js (main thread), mining runs inside a `worker_threads` Worker so the
|
|
20
|
+
* event loop is never blocked. In browser contexts the in-process loop is used
|
|
21
|
+
* until Task #114 wires up the Web Worker entry point.
|
|
22
|
+
*
|
|
23
|
+
* Control-flow invariant (IMPORTANT):
|
|
24
|
+
* WASM and JS are mutually exclusive per chunk — we never re-mine a chunk that
|
|
25
|
+
* WASM already scanned. When WASM returns null (nonce not found in chunk), we
|
|
26
|
+
* advance to the next chunk rather than falling back to JS for the same range.
|
|
27
|
+
* This means `source` is always accurate: 'wasm' when the binary was used for
|
|
28
|
+
* the winning chunk; 'js' when the JS path found the nonce.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.isLessThan = isLessThan;
|
|
32
|
+
exports.buildHeaderTail = buildHeaderTail;
|
|
33
|
+
exports.setBrowserWorkerUrl = setBrowserWorkerUrl;
|
|
34
|
+
exports.mineHeaderTail = mineHeaderTail;
|
|
35
|
+
exports.mineTxPoWInProcess = mineTxPoWInProcess;
|
|
36
|
+
exports.mineTxPoW = mineTxPoW;
|
|
37
|
+
const core_1 = require("@totemsdk/core");
|
|
38
|
+
const core_2 = require("@totemsdk/core");
|
|
39
|
+
const magic_js_1 = require("./magic.js");
|
|
40
|
+
const constants_js_1 = require("./constants.js");
|
|
41
|
+
const mine_wasm_js_1 = require("./mine-wasm.js");
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
43
|
+
// Internal helpers
|
|
44
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Big-endian 256-bit comparison: true if a < b.
|
|
47
|
+
* Used to check: txpowId < mTxnDifficulty.
|
|
48
|
+
*/
|
|
49
|
+
function isLessThan(a, b) {
|
|
50
|
+
for (let i = 0; i < 32; i++) {
|
|
51
|
+
if (a[i] < b[i])
|
|
52
|
+
return true;
|
|
53
|
+
if (a[i] > b[i])
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the first nonce value that would require more bytes in its
|
|
60
|
+
* MiniNumber encoding (i.e. the next boundary at 128, 32768, 8388608, …).
|
|
61
|
+
*
|
|
62
|
+
* Pattern: bigIntToByteArray(n) grows by 1 byte at 2^(8k−1) for k=1,2,3,…
|
|
63
|
+
* k=1: 2^7 = 128
|
|
64
|
+
* k=2: 2^15 = 32768
|
|
65
|
+
* k=3: 2^23 = 8388608
|
|
66
|
+
*/
|
|
67
|
+
function nextNonceBoundary(nonce) {
|
|
68
|
+
const nonceValueLen = (0, core_2.writeMiniNumber)(nonce, 0).length - 2;
|
|
69
|
+
return 2n ** BigInt(8 * nonceValueLen - 1);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build the "header tail" — the part of TxHeader that follows mNonce.
|
|
73
|
+
*
|
|
74
|
+
* This is computed once at the start of a mine and never changes during it.
|
|
75
|
+
*
|
|
76
|
+
* This is the fresh-transaction header shape (blockNumber=0, MAX_HASH block
|
|
77
|
+
* difficulty, zero super-parents, zero MMR, zero customHash). Block-candidate
|
|
78
|
+
* mining (Machine Work Admission) builds its own tail via
|
|
79
|
+
* `admission/template.ts` with real chain state and a customHash commitment.
|
|
80
|
+
*/
|
|
81
|
+
function buildHeaderTail(txBodyHash, timeMilli) {
|
|
82
|
+
return (0, core_2.concat)((0, core_2.writeMiniData)(constants_js_1.MAIN_NET_CHAIN_ID), (0, core_2.writeMiniNumber)(timeMilli, 0), (0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniData)(constants_js_1.MAX_HASH), new Uint8Array([constants_js_1.CASCADE_LEVELS]), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeMiniNumber)(0n, 0), (0, magic_js_1.serializeMagic)(), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeHashToStream)(txBodyHash));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Synchronously mine a chunk of nonces within a fixed encoding boundary.
|
|
86
|
+
*
|
|
87
|
+
* Mutates headerBuf in place — only the nonce value bytes at offset 2 change.
|
|
88
|
+
* Returns the found nonce, or null if not found in this chunk.
|
|
89
|
+
*/
|
|
90
|
+
function mineChunkJs(headerBuf, nonceValueLen, target, startNonce, endNonce) {
|
|
91
|
+
const nonceValueOffset = 2;
|
|
92
|
+
for (let nonce = startNonce; nonce < endNonce; nonce++) {
|
|
93
|
+
let n = nonce;
|
|
94
|
+
for (let i = nonceValueLen - 1; i >= 0; i--) {
|
|
95
|
+
headerBuf[nonceValueOffset + i] = Number(n & 0xffn);
|
|
96
|
+
n >>= 8n;
|
|
97
|
+
}
|
|
98
|
+
const hash = (0, core_1.sha3_256)(headerBuf);
|
|
99
|
+
if (isLessThan(hash, target))
|
|
100
|
+
return nonce;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
// Browser Web Worker dispatch (optional URL-based)
|
|
106
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
107
|
+
let _browserWorkerUrl = null;
|
|
108
|
+
/**
|
|
109
|
+
* Configure the URL of the browser.worker.js bundle so that `mineTxPoW()`
|
|
110
|
+
* can spawn a Web Worker in browser contexts.
|
|
111
|
+
*
|
|
112
|
+
* Call this once at extension/dApp startup. The bundled worker file exposes
|
|
113
|
+
* the same message protocol as node.worker.ts.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* import { setBrowserWorkerUrl } from '@totemsdk/txpow';
|
|
117
|
+
* setBrowserWorkerUrl(browser.runtime.getURL('mine-worker.js'));
|
|
118
|
+
*/
|
|
119
|
+
function setBrowserWorkerUrl(url) {
|
|
120
|
+
_browserWorkerUrl = url;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Spawn a browser Web Worker to run the mining loop off the main thread.
|
|
124
|
+
* Requires a prior call to setBrowserWorkerUrl() to configure the worker URL.
|
|
125
|
+
*/
|
|
126
|
+
async function mineTxPoWViaBrowserWorker(txBodyBytes, txnDifficulty, options) {
|
|
127
|
+
if (!_browserWorkerUrl) {
|
|
128
|
+
throw new Error('Browser Web Worker URL not configured. Call setBrowserWorkerUrl() first.');
|
|
129
|
+
}
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
const WorkerClass = globalThis.Worker;
|
|
132
|
+
const worker = new WorkerClass(_browserWorkerUrl);
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
let settled = false;
|
|
135
|
+
const settle = (fn) => {
|
|
136
|
+
if (!settled) {
|
|
137
|
+
settled = true;
|
|
138
|
+
fn();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
if (options?.signal) {
|
|
142
|
+
const abortHandler = () => {
|
|
143
|
+
settle(() => reject(new DOMException('Mining aborted', 'AbortError')));
|
|
144
|
+
worker.terminate();
|
|
145
|
+
};
|
|
146
|
+
if (options.signal.aborted) {
|
|
147
|
+
abortHandler();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
options.signal.addEventListener('abort', abortHandler, { once: true });
|
|
151
|
+
}
|
|
152
|
+
worker.onmessage = (e) => {
|
|
153
|
+
const msg = e.data;
|
|
154
|
+
if (msg['error']) {
|
|
155
|
+
settle(() => reject(new Error(String(msg['error']))));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
settle(() => resolve({
|
|
159
|
+
minedHeaderBytes: Uint8Array.from(msg['minedHeaderBytes']),
|
|
160
|
+
txpowId: Uint8Array.from(msg['txpowId']),
|
|
161
|
+
nonce: BigInt(msg['nonce']),
|
|
162
|
+
elapsedMs: msg['elapsedMs'],
|
|
163
|
+
source: msg['source'],
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
worker.onerror = (e) => {
|
|
168
|
+
settle(() => reject(new Error(e.message)));
|
|
169
|
+
};
|
|
170
|
+
// Pass the WASM URL so the worker can call setWasmUrl() in its own JS context.
|
|
171
|
+
// Web Workers have isolated module state — the main thread's setWasmUrl() call
|
|
172
|
+
// does not carry over. Without this the worker mines in pure JS.
|
|
173
|
+
const wasmUrl = (0, mine_wasm_js_1.getBrowserWasmUrl)();
|
|
174
|
+
worker.postMessage({
|
|
175
|
+
txBodyBytes: Array.from(txBodyBytes),
|
|
176
|
+
txnDifficulty: Array.from(txnDifficulty),
|
|
177
|
+
wasmUrl: wasmUrl ?? undefined,
|
|
178
|
+
chunkSize: options?.chunkSize,
|
|
179
|
+
maxIterations: options?.maxIterations !== undefined
|
|
180
|
+
? options.maxIterations.toString()
|
|
181
|
+
: undefined,
|
|
182
|
+
timeMilli: options?.timeMilli !== undefined
|
|
183
|
+
? options.timeMilli.toString()
|
|
184
|
+
: undefined,
|
|
185
|
+
forceJs: options?.forceJs,
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
+
// Node.js worker_threads dispatch
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
192
|
+
/**
|
|
193
|
+
* Returns true when mining should be delegated to a Node.js worker_threads Worker.
|
|
194
|
+
*
|
|
195
|
+
* Conditions for using a worker:
|
|
196
|
+
* - Running in Node.js (process.versions.node is set)
|
|
197
|
+
* - Not already inside a worker thread (isMainThread === true)
|
|
198
|
+
* - _skipWorker option is not set
|
|
199
|
+
* - Not in a Jest test environment (ts-jest runs from source, dist/workers/*.js
|
|
200
|
+
* may not exist yet; Jest's own worker processes also have isMainThread=false
|
|
201
|
+
* which handles the common --maxWorkers case automatically)
|
|
202
|
+
*/
|
|
203
|
+
function shouldUseNodeWorker(options) {
|
|
204
|
+
if (options?._skipWorker)
|
|
205
|
+
return false;
|
|
206
|
+
if (typeof process === 'undefined' || !process.versions?.node)
|
|
207
|
+
return false;
|
|
208
|
+
// In Jest, skip worker spawning: source workers are not compiled .js files
|
|
209
|
+
// and Jest's own worker pool already sets isMainThread=false for most runners.
|
|
210
|
+
if (process.env.NODE_ENV === 'test' ||
|
|
211
|
+
process.env.JEST_WORKER_ID !== undefined) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
216
|
+
const wt = require('worker_threads');
|
|
217
|
+
return wt.isMainThread;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Dispatch mining to a Node.js `worker_threads` Worker.
|
|
225
|
+
* The worker runs `mineTxPoWInProcess` with `_skipWorker: true` to prevent
|
|
226
|
+
* recursive worker spawning.
|
|
227
|
+
*/
|
|
228
|
+
async function mineTxPoWViaNodeWorker(txBodyBytes, txnDifficulty, options) {
|
|
229
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
230
|
+
const { Worker } = require('worker_threads');
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
232
|
+
const { join } = require('path');
|
|
233
|
+
const workerPath = join(__dirname, 'workers', 'node.worker.js');
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
let settled = false;
|
|
236
|
+
const settle = (fn) => {
|
|
237
|
+
if (!settled) {
|
|
238
|
+
settled = true;
|
|
239
|
+
fn();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const worker = new Worker(workerPath, {
|
|
243
|
+
workerData: {
|
|
244
|
+
txBodyBytes: Array.from(txBodyBytes),
|
|
245
|
+
txnDifficulty: Array.from(txnDifficulty),
|
|
246
|
+
chunkSize: options?.chunkSize,
|
|
247
|
+
maxIterations: options?.maxIterations !== undefined
|
|
248
|
+
? options.maxIterations.toString()
|
|
249
|
+
: undefined,
|
|
250
|
+
timeMilli: options?.timeMilli !== undefined
|
|
251
|
+
? options.timeMilli.toString()
|
|
252
|
+
: undefined,
|
|
253
|
+
forceJs: options?.forceJs,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
if (options?.signal) {
|
|
257
|
+
const abortHandler = () => {
|
|
258
|
+
settle(() => reject(new DOMException('Mining aborted', 'AbortError')));
|
|
259
|
+
worker.terminate().catch(() => { });
|
|
260
|
+
};
|
|
261
|
+
if (options.signal.aborted) {
|
|
262
|
+
abortHandler();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
options.signal.addEventListener('abort', abortHandler, { once: true });
|
|
266
|
+
}
|
|
267
|
+
worker.on('message', (msg) => {
|
|
268
|
+
if (msg['error']) {
|
|
269
|
+
settle(() => reject(new Error(String(msg['error']))));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
settle(() => resolve({
|
|
273
|
+
minedHeaderBytes: Uint8Array.from(msg['minedHeaderBytes']),
|
|
274
|
+
txpowId: Uint8Array.from(msg['txpowId']),
|
|
275
|
+
nonce: BigInt(msg['nonce']),
|
|
276
|
+
elapsedMs: msg['elapsedMs'],
|
|
277
|
+
source: msg['source'],
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
worker.on('error', (err) => settle(() => reject(err)));
|
|
282
|
+
worker.on('exit', (code) => {
|
|
283
|
+
if (code !== 0) {
|
|
284
|
+
settle(() => reject(new Error(`Mining worker exited with non-zero code ${code}`)));
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
290
|
+
// In-process mining loop
|
|
291
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
292
|
+
/**
|
|
293
|
+
* Core mining loop over a pre-built header tail.
|
|
294
|
+
*
|
|
295
|
+
* Iterates the nonce in the serialized TxHeader (nonce value at offset 2,
|
|
296
|
+
* followed by `headerTail`) until `SHA3-256(header) < target`. Shared by the
|
|
297
|
+
* transaction miner (`mineTxPoWInProcess`) and the Machine Work Admission
|
|
298
|
+
* miner (`admission/mine.ts`), which supplies its own block-candidate tail.
|
|
299
|
+
*
|
|
300
|
+
* @param headerTail Serialized TxHeader bytes AFTER the nonce field.
|
|
301
|
+
* @param target 32-byte difficulty target (big-endian 256-bit).
|
|
302
|
+
* @param options Chunk size, max iterations, abort signal, timeMilli override.
|
|
303
|
+
*/
|
|
304
|
+
async function mineHeaderTail(headerTail, target, options) {
|
|
305
|
+
const chunkSize = BigInt(options?.chunkSize ?? 10000);
|
|
306
|
+
const maxIterations = options?.maxIterations !== undefined
|
|
307
|
+
? BigInt(options.maxIterations)
|
|
308
|
+
: undefined;
|
|
309
|
+
const signal = options?.signal;
|
|
310
|
+
const forceJs = options?.forceJs ?? false;
|
|
311
|
+
const useWasm = !forceJs && (await (0, mine_wasm_js_1.isWasmAvailable)());
|
|
312
|
+
const startTime = Date.now();
|
|
313
|
+
let totalIterations = 0n;
|
|
314
|
+
let nonce = 0n;
|
|
315
|
+
while (true) {
|
|
316
|
+
if (signal?.aborted) {
|
|
317
|
+
throw new DOMException('Mining aborted', 'AbortError');
|
|
318
|
+
}
|
|
319
|
+
if (maxIterations !== undefined && totalIterations >= maxIterations) {
|
|
320
|
+
throw new Error(`Mining exhausted: ${totalIterations} iterations without finding a valid nonce`);
|
|
321
|
+
}
|
|
322
|
+
const boundary = nextNonceBoundary(nonce);
|
|
323
|
+
const chunkEnd = nonce + chunkSize < boundary ? nonce + chunkSize : boundary;
|
|
324
|
+
const nonceBytes = (0, core_2.writeMiniNumber)(nonce, 0);
|
|
325
|
+
const nonceValueLen = nonceBytes.length - 2;
|
|
326
|
+
const headerBuf = (0, core_2.concat)(nonceBytes, headerTail);
|
|
327
|
+
// ── CRITICAL: WASM and JS are mutually exclusive per chunk ──────────────
|
|
328
|
+
// When WASM returns null (no nonce in chunk), the chunk has been fully
|
|
329
|
+
// scanned by WASM — we do NOT re-scan it with JS. Doing so would double
|
|
330
|
+
// work and could incorrectly set source='wasm' on a JS-found nonce.
|
|
331
|
+
let found;
|
|
332
|
+
if (useWasm) {
|
|
333
|
+
found = await (0, mine_wasm_js_1.mineChunkWasm)(headerBuf.slice(), // WASM gets its own copy to avoid aliasing
|
|
334
|
+
2, nonceValueLen, target, nonce, chunkEnd - nonce);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
found = mineChunkJs(headerBuf, nonceValueLen, target, nonce, chunkEnd);
|
|
338
|
+
}
|
|
339
|
+
// ── End mutually exclusive section ───────────────────────────────────────
|
|
340
|
+
if (found !== null) {
|
|
341
|
+
const finalNonceBytes = (0, core_2.writeMiniNumber)(found, 0);
|
|
342
|
+
const finalHeader = (0, core_2.concat)(finalNonceBytes, headerTail);
|
|
343
|
+
const txpowId = (0, core_1.sha3_256)(finalHeader);
|
|
344
|
+
return {
|
|
345
|
+
minedHeaderBytes: finalHeader,
|
|
346
|
+
txpowId,
|
|
347
|
+
nonce: found,
|
|
348
|
+
elapsedMs: Date.now() - startTime,
|
|
349
|
+
source: useWasm ? 'wasm' : 'js',
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
totalIterations += chunkEnd - nonce;
|
|
353
|
+
nonce = chunkEnd;
|
|
354
|
+
await new Promise(resolve => {
|
|
355
|
+
if (typeof setImmediate !== 'undefined') {
|
|
356
|
+
setImmediate(resolve);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
setTimeout(resolve, 0);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Run the mining loop in the current thread/task.
|
|
366
|
+
* Called directly by the Node.js worker, and by the browser code path.
|
|
367
|
+
*/
|
|
368
|
+
async function mineTxPoWInProcess(txBodyBytes, txnDifficulty, options) {
|
|
369
|
+
const timeMilli = options?.timeMilli ?? BigInt(Date.now());
|
|
370
|
+
const bodyHash = (0, core_1.sha3_256)(txBodyBytes);
|
|
371
|
+
const headerTail = buildHeaderTail(bodyHash, timeMilli);
|
|
372
|
+
return mineHeaderTail(headerTail, txnDifficulty, options);
|
|
373
|
+
}
|
|
374
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
375
|
+
// Public API
|
|
376
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
377
|
+
/**
|
|
378
|
+
* Mine a TxPoW locally by iterating the header nonce until
|
|
379
|
+
* SHA3-256( TxHeader ) < txnDifficulty
|
|
380
|
+
*
|
|
381
|
+
* In Node.js (main thread), delegates to a `worker_threads` Worker so the
|
|
382
|
+
* event loop is never blocked. In browsers or Jest, runs in-process with
|
|
383
|
+
* periodic `setImmediate`/`setTimeout(0)` yields between chunks.
|
|
384
|
+
*
|
|
385
|
+
* Uses the pre-compiled `miner.wasm` binary for inner-loop throughput when
|
|
386
|
+
* available. Falls back to pure JS automatically when the binary is absent.
|
|
387
|
+
*
|
|
388
|
+
* @param txBodyBytes Pre-serialized TxBody bytes (from serializeTxBody).
|
|
389
|
+
* @param txnDifficulty 32-byte target. MUST be ≤ TX_POW_MIN_DIFFICULTY.
|
|
390
|
+
* @param options Chunk size, max iterations, abort signal, timeMilli override.
|
|
391
|
+
*/
|
|
392
|
+
async function mineTxPoW(txBodyBytes, txnDifficulty, options) {
|
|
393
|
+
// ── Node.js: spawn a worker_threads Worker (keeps event loop unblocked) ──
|
|
394
|
+
if (shouldUseNodeWorker(options)) {
|
|
395
|
+
return mineTxPoWViaNodeWorker(txBodyBytes, txnDifficulty, options);
|
|
396
|
+
}
|
|
397
|
+
// ── Browser: spawn a Web Worker when the URL has been configured ──────────
|
|
398
|
+
// The extension build (Task #114) calls setBrowserWorkerUrl() at startup.
|
|
399
|
+
// Without a configured URL, mine falls back to in-process (setImmediate loop).
|
|
400
|
+
if (!options?._skipWorker &&
|
|
401
|
+
typeof process === 'undefined' && // not Node.js
|
|
402
|
+
_browserWorkerUrl !== null) {
|
|
403
|
+
return mineTxPoWViaBrowserWorker(txBodyBytes, txnDifficulty, options);
|
|
404
|
+
}
|
|
405
|
+
// ── In-process fallback (browser without worker URL, or explicit skip) ────
|
|
406
|
+
return mineTxPoWInProcess(txBodyBytes, txnDifficulty, options);
|
|
407
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* serialization.ts — TxPoW envelope serialization.
|
|
3
|
+
*
|
|
4
|
+
* Ports serializeTxHeader / serializeTxBody / serializeTxPoW from
|
|
5
|
+
* packages/totem-extension/src/core/transaction/MinimaTransactionBuilder.ts
|
|
6
|
+
* into the standalone @totemsdk/txpow package.
|
|
7
|
+
*
|
|
8
|
+
* KEY DESIGN DECISIONS
|
|
9
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
10
|
+
* • serializeTxBody accepts PRE-SERIALIZED transaction bytes and witness bytes.
|
|
11
|
+
* Callers use @totemsdk/core's serializeTransaction() + their own witness
|
|
12
|
+
* serializer, then pass the byte arrays here. This keeps @totemsdk/txpow
|
|
13
|
+
* free of WotsSignatureData / InputScriptInfo / CoinProof dependencies.
|
|
14
|
+
*
|
|
15
|
+
* • txnDifficulty defaults to MAX_HASH (all 0xFF) to produce byte-identical
|
|
16
|
+
* output to the extension's current MEG-mined path. For locally mined TxPoWs
|
|
17
|
+
* the caller MUST pass a value ≤ TX_POW_MIN_DIFFICULTY, otherwise the
|
|
18
|
+
* transaction will fail TxPoWChecker.checkTxPoWSimple() at block inclusion.
|
|
19
|
+
*
|
|
20
|
+
* • nonce defaults to 0n (the mining loop updates it). timeMilli defaults to
|
|
21
|
+
* Date.now() at call time.
|
|
22
|
+
*
|
|
23
|
+
* WIRE FORMAT REFERENCE
|
|
24
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
25
|
+
* TxHeader.writeDataStream():
|
|
26
|
+
* 1. mNonce MiniNumber
|
|
27
|
+
* 2. mChainID MiniData (MAIN_NET = [0x00])
|
|
28
|
+
* 3. mTimeMilli MiniNumber
|
|
29
|
+
* 4. mBlockNumber MiniNumber (0 for fresh TxPoW)
|
|
30
|
+
* 5. mBlockDifficulty MiniData (MAX_HASH for fresh TxPoW)
|
|
31
|
+
* 6. Super Parents RLE: 1 byte count + writeHashToStream(parent)
|
|
32
|
+
* 7. mMMRRoot writeHashToStream (ZERO_HASH for fresh TxPoW)
|
|
33
|
+
* 8. mMMRTotal MiniNumber (0)
|
|
34
|
+
* 9. mMagic Magic.writeDataStream()
|
|
35
|
+
* 10. mCustomHash writeHashToStream (ZERO_HASH)
|
|
36
|
+
* 11. mTxBodyHash writeHashToStream
|
|
37
|
+
*
|
|
38
|
+
* TxBody.writeDataStream():
|
|
39
|
+
* 1. mPRNG writeHashToStream (32 random bytes)
|
|
40
|
+
* 2. mTxnDifficulty MiniData
|
|
41
|
+
* 3. mTransaction Transaction.writeDataStream() (pre-serialized)
|
|
42
|
+
* 4. mWitness Witness.writeDataStream() (pre-serialized)
|
|
43
|
+
* 5. mBurnTransaction Transaction.writeDataStream() (empty)
|
|
44
|
+
* 6. mBurnWitness Witness.writeDataStream() (empty)
|
|
45
|
+
* 7. mTxPowIDList MiniNumber(0)
|
|
46
|
+
*
|
|
47
|
+
* TxPoW.writeDataStream():
|
|
48
|
+
* TxHeader bytes + MiniByte(0x01=hasBody) + TxBody bytes
|
|
49
|
+
*
|
|
50
|
+
* TxPoW ID:
|
|
51
|
+
* SHA3-256( TxHeader bytes ) — Java: Crypto.getInstance().hashObject(mHeader)
|
|
52
|
+
*/
|
|
53
|
+
export interface TxHeaderOptions {
|
|
54
|
+
/** Proof-of-work nonce. Defaults to 0n. The mining loop iterates this. */
|
|
55
|
+
nonce?: bigint;
|
|
56
|
+
/** Block timestamp in milliseconds. Defaults to Date.now() at call time. */
|
|
57
|
+
timeMilli?: bigint;
|
|
58
|
+
}
|
|
59
|
+
export interface TxBodyOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Transaction difficulty target (32-byte MiniData).
|
|
62
|
+
* • MEG-side-mined path: leave undefined → MAX_HASH (all 0xFF)
|
|
63
|
+
* • Locally mined path: MUST be ≤ TX_POW_MIN_DIFFICULTY, typically fetched
|
|
64
|
+
* via fetchTxPowTarget() from the same package.
|
|
65
|
+
* Setting MAX_HASH for locally mined TxPoWs will cause block-level rejection.
|
|
66
|
+
*/
|
|
67
|
+
txnDifficulty?: Uint8Array;
|
|
68
|
+
/** Override the 32-byte PRNG field. Useful for deterministic tests only. */
|
|
69
|
+
prng?: Uint8Array;
|
|
70
|
+
}
|
|
71
|
+
export type TxPoWOptions = TxHeaderOptions & TxBodyOptions;
|
|
72
|
+
/**
|
|
73
|
+
* Serialize a TxHeader per Minima's TxHeader.writeDataStream().
|
|
74
|
+
*
|
|
75
|
+
* @param txBodyHash SHA3-256 of the serialized TxBody (32 bytes)
|
|
76
|
+
* @param options Optional nonce and timeMilli overrides
|
|
77
|
+
*/
|
|
78
|
+
export declare function serializeTxHeader(txBodyHash: Uint8Array, options?: TxHeaderOptions): Uint8Array;
|
|
79
|
+
/**
|
|
80
|
+
* Compute TxPoW ID = SHA3-256(TxHeader bytes).
|
|
81
|
+
* Matches Java: Crypto.getInstance().hashObject(mHeader) via SHA3Digest(256).
|
|
82
|
+
*/
|
|
83
|
+
export declare function computeTxPoWId(headerBytes: Uint8Array): Uint8Array;
|
|
84
|
+
/**
|
|
85
|
+
* Serialize a TxBody per Minima's TxBody.writeDataStream().
|
|
86
|
+
*
|
|
87
|
+
* @param txBytes Pre-serialized Transaction bytes (from @totemsdk/core's serializeTransaction)
|
|
88
|
+
* @param witnessBytes Pre-serialized Witness bytes (from extension's serializeWitness)
|
|
89
|
+
* @param options Optional txnDifficulty override and test PRNG
|
|
90
|
+
*/
|
|
91
|
+
export declare function serializeTxBody(txBytes: Uint8Array, witnessBytes: Uint8Array, options?: TxBodyOptions): Uint8Array;
|
|
92
|
+
/**
|
|
93
|
+
* Serialize a complete TxPoW per Minima's TxPoW.writeDataStream().
|
|
94
|
+
*
|
|
95
|
+
* Assembles TxBody from pre-serialized tx+witness bytes, hashes the body to
|
|
96
|
+
* obtain mTxBodyHash, builds TxHeader with nonce=0, then concatenates:
|
|
97
|
+
* TxHeader | 0x01 (hasBody) | TxBody
|
|
98
|
+
*
|
|
99
|
+
* The returned bytes have nonce=0. Pass them to mineTxPoW() to find a valid
|
|
100
|
+
* nonce for local mining, or send as-is when MEG will re-mine.
|
|
101
|
+
*
|
|
102
|
+
* @param txBytes Pre-serialized Transaction bytes
|
|
103
|
+
* @param witnessBytes Pre-serialized Witness bytes
|
|
104
|
+
* @param options Optional txnDifficulty, nonce, timeMilli, prng
|
|
105
|
+
*/
|
|
106
|
+
export declare function serializeTxPoW(txBytes: Uint8Array, witnessBytes: Uint8Array, options?: TxPoWOptions): Uint8Array;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* serialization.ts — TxPoW envelope serialization.
|
|
4
|
+
*
|
|
5
|
+
* Ports serializeTxHeader / serializeTxBody / serializeTxPoW from
|
|
6
|
+
* packages/totem-extension/src/core/transaction/MinimaTransactionBuilder.ts
|
|
7
|
+
* into the standalone @totemsdk/txpow package.
|
|
8
|
+
*
|
|
9
|
+
* KEY DESIGN DECISIONS
|
|
10
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
11
|
+
* • serializeTxBody accepts PRE-SERIALIZED transaction bytes and witness bytes.
|
|
12
|
+
* Callers use @totemsdk/core's serializeTransaction() + their own witness
|
|
13
|
+
* serializer, then pass the byte arrays here. This keeps @totemsdk/txpow
|
|
14
|
+
* free of WotsSignatureData / InputScriptInfo / CoinProof dependencies.
|
|
15
|
+
*
|
|
16
|
+
* • txnDifficulty defaults to MAX_HASH (all 0xFF) to produce byte-identical
|
|
17
|
+
* output to the extension's current MEG-mined path. For locally mined TxPoWs
|
|
18
|
+
* the caller MUST pass a value ≤ TX_POW_MIN_DIFFICULTY, otherwise the
|
|
19
|
+
* transaction will fail TxPoWChecker.checkTxPoWSimple() at block inclusion.
|
|
20
|
+
*
|
|
21
|
+
* • nonce defaults to 0n (the mining loop updates it). timeMilli defaults to
|
|
22
|
+
* Date.now() at call time.
|
|
23
|
+
*
|
|
24
|
+
* WIRE FORMAT REFERENCE
|
|
25
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
26
|
+
* TxHeader.writeDataStream():
|
|
27
|
+
* 1. mNonce MiniNumber
|
|
28
|
+
* 2. mChainID MiniData (MAIN_NET = [0x00])
|
|
29
|
+
* 3. mTimeMilli MiniNumber
|
|
30
|
+
* 4. mBlockNumber MiniNumber (0 for fresh TxPoW)
|
|
31
|
+
* 5. mBlockDifficulty MiniData (MAX_HASH for fresh TxPoW)
|
|
32
|
+
* 6. Super Parents RLE: 1 byte count + writeHashToStream(parent)
|
|
33
|
+
* 7. mMMRRoot writeHashToStream (ZERO_HASH for fresh TxPoW)
|
|
34
|
+
* 8. mMMRTotal MiniNumber (0)
|
|
35
|
+
* 9. mMagic Magic.writeDataStream()
|
|
36
|
+
* 10. mCustomHash writeHashToStream (ZERO_HASH)
|
|
37
|
+
* 11. mTxBodyHash writeHashToStream
|
|
38
|
+
*
|
|
39
|
+
* TxBody.writeDataStream():
|
|
40
|
+
* 1. mPRNG writeHashToStream (32 random bytes)
|
|
41
|
+
* 2. mTxnDifficulty MiniData
|
|
42
|
+
* 3. mTransaction Transaction.writeDataStream() (pre-serialized)
|
|
43
|
+
* 4. mWitness Witness.writeDataStream() (pre-serialized)
|
|
44
|
+
* 5. mBurnTransaction Transaction.writeDataStream() (empty)
|
|
45
|
+
* 6. mBurnWitness Witness.writeDataStream() (empty)
|
|
46
|
+
* 7. mTxPowIDList MiniNumber(0)
|
|
47
|
+
*
|
|
48
|
+
* TxPoW.writeDataStream():
|
|
49
|
+
* TxHeader bytes + MiniByte(0x01=hasBody) + TxBody bytes
|
|
50
|
+
*
|
|
51
|
+
* TxPoW ID:
|
|
52
|
+
* SHA3-256( TxHeader bytes ) — Java: Crypto.getInstance().hashObject(mHeader)
|
|
53
|
+
*/
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.serializeTxHeader = serializeTxHeader;
|
|
56
|
+
exports.computeTxPoWId = computeTxPoWId;
|
|
57
|
+
exports.serializeTxBody = serializeTxBody;
|
|
58
|
+
exports.serializeTxPoW = serializeTxPoW;
|
|
59
|
+
const core_1 = require("@totemsdk/core");
|
|
60
|
+
const core_2 = require("@totemsdk/core");
|
|
61
|
+
const magic_js_1 = require("./magic.js");
|
|
62
|
+
const constants_js_1 = require("./constants.js");
|
|
63
|
+
/**
|
|
64
|
+
* Serialize a TxHeader per Minima's TxHeader.writeDataStream().
|
|
65
|
+
*
|
|
66
|
+
* @param txBodyHash SHA3-256 of the serialized TxBody (32 bytes)
|
|
67
|
+
* @param options Optional nonce and timeMilli overrides
|
|
68
|
+
*/
|
|
69
|
+
function serializeTxHeader(txBodyHash, options) {
|
|
70
|
+
const nonce = options?.nonce ?? 0n;
|
|
71
|
+
const timeMilli = options?.timeMilli ?? BigInt(Date.now());
|
|
72
|
+
const parts = [];
|
|
73
|
+
parts.push((0, core_2.writeMiniNumber)(nonce, 0));
|
|
74
|
+
parts.push((0, core_2.writeMiniData)(constants_js_1.MAIN_NET_CHAIN_ID));
|
|
75
|
+
parts.push((0, core_2.writeMiniNumber)(timeMilli, 0));
|
|
76
|
+
parts.push((0, core_2.writeMiniNumber)(0n, 0));
|
|
77
|
+
parts.push((0, core_2.writeMiniData)(constants_js_1.MAX_HASH));
|
|
78
|
+
parts.push(new Uint8Array([constants_js_1.CASCADE_LEVELS]));
|
|
79
|
+
parts.push((0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
80
|
+
parts.push((0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
81
|
+
parts.push((0, core_2.writeMiniNumber)(0n, 0));
|
|
82
|
+
parts.push((0, magic_js_1.serializeMagic)());
|
|
83
|
+
parts.push((0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
84
|
+
parts.push((0, core_2.writeHashToStream)(txBodyHash));
|
|
85
|
+
return (0, core_2.concat)(...parts);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compute TxPoW ID = SHA3-256(TxHeader bytes).
|
|
89
|
+
* Matches Java: Crypto.getInstance().hashObject(mHeader) via SHA3Digest(256).
|
|
90
|
+
*/
|
|
91
|
+
function computeTxPoWId(headerBytes) {
|
|
92
|
+
return (0, core_1.sha3_256)(headerBytes);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build the empty burn transaction bytes.
|
|
96
|
+
*
|
|
97
|
+
* Equivalent to serializeTransaction({ linkHash: new Uint8Array([0x00]),
|
|
98
|
+
* inputs: [], outputs: [], state: [] })
|
|
99
|
+
* which produces:
|
|
100
|
+
* writeMiniNumber(0n) inputs count
|
|
101
|
+
* writeMiniNumber(0n) outputs count
|
|
102
|
+
* writeMiniNumber(0n) state count
|
|
103
|
+
* writeHashToStream([0x00]) linkHash = ZERO_TXPOWID (1 byte)
|
|
104
|
+
*/
|
|
105
|
+
function buildEmptyBurnTxBytes() {
|
|
106
|
+
return (0, core_2.concat)((0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeHashToStream)(new Uint8Array([0x00])));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Build the empty burn witness bytes.
|
|
110
|
+
*
|
|
111
|
+
* Equivalent to serializeWitness() with no arguments:
|
|
112
|
+
* writeMiniNumber(0n) signature count
|
|
113
|
+
* writeMiniNumber(0n) coinproof count
|
|
114
|
+
* writeMiniNumber(0n) scriptproof count
|
|
115
|
+
*/
|
|
116
|
+
function buildEmptyBurnWitnessBytes() {
|
|
117
|
+
return (0, core_2.concat)((0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniNumber)(0n, 0));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Serialize a TxBody per Minima's TxBody.writeDataStream().
|
|
121
|
+
*
|
|
122
|
+
* @param txBytes Pre-serialized Transaction bytes (from @totemsdk/core's serializeTransaction)
|
|
123
|
+
* @param witnessBytes Pre-serialized Witness bytes (from extension's serializeWitness)
|
|
124
|
+
* @param options Optional txnDifficulty override and test PRNG
|
|
125
|
+
*/
|
|
126
|
+
function serializeTxBody(txBytes, witnessBytes, options) {
|
|
127
|
+
const txnDifficulty = options?.txnDifficulty ?? constants_js_1.MAX_HASH;
|
|
128
|
+
let prng;
|
|
129
|
+
if (options?.prng) {
|
|
130
|
+
prng = options.prng;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const fresh = new Uint8Array(32);
|
|
134
|
+
if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
|
|
135
|
+
throw new Error('SECURITY: crypto.getRandomValues unavailable — cannot generate secure random bytes for transaction PRNG');
|
|
136
|
+
}
|
|
137
|
+
crypto.getRandomValues(fresh);
|
|
138
|
+
prng = fresh;
|
|
139
|
+
}
|
|
140
|
+
return (0, core_2.concat)((0, core_2.writeHashToStream)(prng), (0, core_2.writeMiniData)(txnDifficulty), txBytes, witnessBytes, buildEmptyBurnTxBytes(), buildEmptyBurnWitnessBytes(), (0, core_2.writeMiniNumber)(0n, 0));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Serialize a complete TxPoW per Minima's TxPoW.writeDataStream().
|
|
144
|
+
*
|
|
145
|
+
* Assembles TxBody from pre-serialized tx+witness bytes, hashes the body to
|
|
146
|
+
* obtain mTxBodyHash, builds TxHeader with nonce=0, then concatenates:
|
|
147
|
+
* TxHeader | 0x01 (hasBody) | TxBody
|
|
148
|
+
*
|
|
149
|
+
* The returned bytes have nonce=0. Pass them to mineTxPoW() to find a valid
|
|
150
|
+
* nonce for local mining, or send as-is when MEG will re-mine.
|
|
151
|
+
*
|
|
152
|
+
* @param txBytes Pre-serialized Transaction bytes
|
|
153
|
+
* @param witnessBytes Pre-serialized Witness bytes
|
|
154
|
+
* @param options Optional txnDifficulty, nonce, timeMilli, prng
|
|
155
|
+
*/
|
|
156
|
+
function serializeTxPoW(txBytes, witnessBytes, options) {
|
|
157
|
+
const txBody = serializeTxBody(txBytes, witnessBytes, options);
|
|
158
|
+
const txBodyHash = (0, core_1.sha3_256)(txBody);
|
|
159
|
+
return (0, core_2.concat)(serializeTxHeader(txBodyHash, options), new Uint8Array([0x01]), txBody);
|
|
160
|
+
}
|