ox 0.14.18 → 0.14.19
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/CHANGELOG.md +6 -0
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/tempo/VirtualMaster.js +192 -13
- package/_cjs/tempo/VirtualMaster.js.map +1 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/tempo/internal/mine.wasm.js +6 -0
- package/_cjs/tempo/internal/mine.wasm.js.map +1 -0
- package/_cjs/tempo/internal/virtualMasterPool.js +186 -0
- package/_cjs/tempo/internal/virtualMasterPool.js.map +1 -0
- package/_cjs/version.js +1 -1
- package/_esm/tempo/TxEnvelopeTempo.js +6 -3
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/tempo/VirtualMaster.js +305 -22
- package/_esm/tempo/VirtualMaster.js.map +1 -1
- package/_esm/tempo/index.js +4 -2
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/tempo/internal/mine.wasm.js +15 -0
- package/_esm/tempo/internal/mine.wasm.js.map +1 -0
- package/_esm/tempo/internal/virtualMasterPool.js +216 -0
- package/_esm/tempo/internal/virtualMasterPool.js.map +1 -0
- package/_esm/version.js +1 -1
- package/_types/tempo/TxEnvelopeTempo.d.ts +6 -3
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/tempo/VirtualMaster.d.ts +92 -10
- package/_types/tempo/VirtualMaster.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +4 -2
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/tempo/internal/mine.wasm.d.ts +5 -0
- package/_types/tempo/internal/mine.wasm.d.ts.map +1 -0
- package/_types/tempo/internal/virtualMasterPool.d.ts +46 -0
- package/_types/tempo/internal/virtualMasterPool.d.ts.map +1 -0
- package/_types/version.d.ts +1 -1
- package/package.json +1 -1
- package/tempo/TxEnvelopeTempo.ts +6 -3
- package/tempo/VirtualMaster.test.ts +77 -0
- package/tempo/VirtualMaster.ts +431 -23
- package/tempo/index.ts +5 -2
- package/tempo/internal/mine.c +182 -0
- package/tempo/internal/mine.wasm.ts +17 -0
- package/tempo/internal/virtualMasterPool.ts +254 -0
- package/version.ts +1 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// TIP-1022 salt mining — isomorphic WASM (Node + browser).
|
|
2
|
+
// Specialized single-block keccak256 for 52-byte input.
|
|
3
|
+
//
|
|
4
|
+
// Memory layout at offset 1024:
|
|
5
|
+
// [0..19] address (set by JS)
|
|
6
|
+
// [20..51] salt (set by JS, incremented in-place)
|
|
7
|
+
// [52..83] hash out (written on match)
|
|
8
|
+
|
|
9
|
+
typedef unsigned char uint8_t;
|
|
10
|
+
typedef unsigned int uint32_t;
|
|
11
|
+
typedef unsigned long long uint64_t;
|
|
12
|
+
|
|
13
|
+
#define DATA_OFFSET 1024
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Keccak-f[1600] — fully unrolled, native uint64_t → wasm i64.
|
|
17
|
+
// Based on XKCP / hash-wasm structure.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
#define ROTL64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
|
|
21
|
+
|
|
22
|
+
static const uint64_t RC[24] = {
|
|
23
|
+
0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
|
|
24
|
+
0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
|
|
25
|
+
0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
|
|
26
|
+
0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
|
|
27
|
+
0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
|
|
28
|
+
0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
|
|
29
|
+
0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
|
|
30
|
+
0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Theta — fully unrolled column parity + diffusion
|
|
34
|
+
#define XORED(i) (A[(i)] ^ A[(i)+5] ^ A[(i)+10] ^ A[(i)+15] ^ A[(i)+20])
|
|
35
|
+
|
|
36
|
+
static inline void theta(uint64_t *A) {
|
|
37
|
+
uint64_t D[5];
|
|
38
|
+
D[0] = ROTL64(XORED(1), 1) ^ XORED(4);
|
|
39
|
+
D[1] = ROTL64(XORED(2), 1) ^ XORED(0);
|
|
40
|
+
D[2] = ROTL64(XORED(3), 1) ^ XORED(1);
|
|
41
|
+
D[3] = ROTL64(XORED(4), 1) ^ XORED(2);
|
|
42
|
+
D[4] = ROTL64(XORED(0), 1) ^ XORED(3);
|
|
43
|
+
A[ 0]^=D[0]; A[ 1]^=D[1]; A[ 2]^=D[2]; A[ 3]^=D[3]; A[ 4]^=D[4];
|
|
44
|
+
A[ 5]^=D[0]; A[ 6]^=D[1]; A[ 7]^=D[2]; A[ 8]^=D[3]; A[ 9]^=D[4];
|
|
45
|
+
A[10]^=D[0]; A[11]^=D[1]; A[12]^=D[2]; A[13]^=D[3]; A[14]^=D[4];
|
|
46
|
+
A[15]^=D[0]; A[16]^=D[1]; A[17]^=D[2]; A[18]^=D[3]; A[19]^=D[4];
|
|
47
|
+
A[20]^=D[0]; A[21]^=D[1]; A[22]^=D[2]; A[23]^=D[3]; A[24]^=D[4];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Rho — explicit per-lane rotations with compile-time constants
|
|
51
|
+
static inline void rho(uint64_t *A) {
|
|
52
|
+
A[ 1] = ROTL64(A[ 1], 1); A[ 2] = ROTL64(A[ 2], 62);
|
|
53
|
+
A[ 3] = ROTL64(A[ 3], 28); A[ 4] = ROTL64(A[ 4], 27);
|
|
54
|
+
A[ 5] = ROTL64(A[ 5], 36); A[ 6] = ROTL64(A[ 6], 44);
|
|
55
|
+
A[ 7] = ROTL64(A[ 7], 6); A[ 8] = ROTL64(A[ 8], 55);
|
|
56
|
+
A[ 9] = ROTL64(A[ 9], 20); A[10] = ROTL64(A[10], 3);
|
|
57
|
+
A[11] = ROTL64(A[11], 10); A[12] = ROTL64(A[12], 43);
|
|
58
|
+
A[13] = ROTL64(A[13], 25); A[14] = ROTL64(A[14], 39);
|
|
59
|
+
A[15] = ROTL64(A[15], 41); A[16] = ROTL64(A[16], 45);
|
|
60
|
+
A[17] = ROTL64(A[17], 15); A[18] = ROTL64(A[18], 21);
|
|
61
|
+
A[19] = ROTL64(A[19], 8); A[20] = ROTL64(A[20], 18);
|
|
62
|
+
A[21] = ROTL64(A[21], 2); A[22] = ROTL64(A[22], 61);
|
|
63
|
+
A[23] = ROTL64(A[23], 56); A[24] = ROTL64(A[24], 14);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Pi — single-temporary rotation chain (no scratch buffer)
|
|
67
|
+
static inline void pi(uint64_t *A) {
|
|
68
|
+
uint64_t A1 = A[1];
|
|
69
|
+
A[ 1]=A[ 6]; A[ 6]=A[ 9]; A[ 9]=A[22]; A[22]=A[14]; A[14]=A[20];
|
|
70
|
+
A[20]=A[ 2]; A[ 2]=A[12]; A[12]=A[13]; A[13]=A[19]; A[19]=A[23];
|
|
71
|
+
A[23]=A[15]; A[15]=A[ 4]; A[ 4]=A[24]; A[24]=A[21]; A[21]=A[ 8];
|
|
72
|
+
A[ 8]=A[16]; A[16]=A[ 5]; A[ 5]=A[ 3]; A[ 3]=A[18]; A[18]=A[17];
|
|
73
|
+
A[17]=A[11]; A[11]=A[ 7]; A[ 7]=A[10]; A[10]=A1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Chi — unrolled nonlinear step
|
|
77
|
+
#define CHI_ROW(i) { \
|
|
78
|
+
uint64_t a0 = A[(i)], a1 = A[(i)+1]; \
|
|
79
|
+
A[(i) ] ^= ~a1 & A[(i)+2]; \
|
|
80
|
+
A[(i)+1] ^= ~A[(i)+2] & A[(i)+3]; \
|
|
81
|
+
A[(i)+2] ^= ~A[(i)+3] & A[(i)+4]; \
|
|
82
|
+
A[(i)+3] ^= ~A[(i)+4] & a0; \
|
|
83
|
+
A[(i)+4] ^= ~a0 & a1; \
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static inline void chi(uint64_t *A) {
|
|
87
|
+
CHI_ROW(0); CHI_ROW(5); CHI_ROW(10); CHI_ROW(15); CHI_ROW(20);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static void keccak_f1600(uint64_t *A) {
|
|
91
|
+
for (int r = 0; r < 24; r++) {
|
|
92
|
+
theta(A); rho(A); pi(A); chi(A);
|
|
93
|
+
A[0] ^= RC[r];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Helpers
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
static inline uint64_t load64_le(const uint8_t *p) {
|
|
102
|
+
return (uint64_t)p[0] | ((uint64_t)p[1] << 8) |
|
|
103
|
+
((uint64_t)p[2] <<16)| ((uint64_t)p[3] << 24) |
|
|
104
|
+
((uint64_t)p[4] <<32)| ((uint64_t)p[5] << 40) |
|
|
105
|
+
((uint64_t)p[6] <<48)| ((uint64_t)p[7] << 56);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static inline void store64_le(uint8_t *p, uint64_t v) {
|
|
109
|
+
p[0]=(uint8_t)v; p[1]=(uint8_t)(v>>8); p[2]=(uint8_t)(v>>16); p[3]=(uint8_t)(v>>24);
|
|
110
|
+
p[4]=(uint8_t)(v>>32); p[5]=(uint8_t)(v>>40); p[6]=(uint8_t)(v>>48); p[7]=(uint8_t)(v>>56);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static inline uint32_t load32_le(const uint8_t *p) {
|
|
114
|
+
return (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Mining entry point
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
__attribute__((export_name("mine")))
|
|
122
|
+
int mine(int count) {
|
|
123
|
+
uint8_t *mem = (uint8_t *)DATA_OFFSET;
|
|
124
|
+
uint8_t *addr = mem; // 20 bytes
|
|
125
|
+
uint8_t *salt = mem + 20; // 32 bytes
|
|
126
|
+
uint8_t *hout = mem + 52; // 32 bytes
|
|
127
|
+
|
|
128
|
+
// Pre-build base state for 52-byte keccak256 (one block, rate=136).
|
|
129
|
+
// Block = [address(20) | salt(32) | 0x01 | zeros | 0x80]
|
|
130
|
+
// Only lanes 0..6 are non-zero from the message, plus padding in lanes 6 & 16.
|
|
131
|
+
uint64_t base[25];
|
|
132
|
+
for (int i = 0; i < 25; i++) base[i] = 0;
|
|
133
|
+
|
|
134
|
+
// Load address into lanes 0..2 (bytes 0..23, only 0..19 used)
|
|
135
|
+
uint8_t buf[24];
|
|
136
|
+
for (int i = 0; i < 20; i++) buf[i] = addr[i];
|
|
137
|
+
for (int i = 20; i < 24; i++) buf[i] = 0;
|
|
138
|
+
base[0] = load64_le(buf);
|
|
139
|
+
base[1] = load64_le(buf + 8);
|
|
140
|
+
base[2] = load64_le(buf + 16);
|
|
141
|
+
|
|
142
|
+
// Keccak padding: byte 52 = 0x01, byte 135 = 0x80
|
|
143
|
+
// Lane 6 (bytes 48..55): byte 52 = lane offset 4 → bit 32
|
|
144
|
+
base[6] = (uint64_t)0x01 << 32;
|
|
145
|
+
// Lane 16 (bytes 128..135): byte 135 = lane offset 7 → bit 56
|
|
146
|
+
base[16] = (uint64_t)0x80 << 56;
|
|
147
|
+
|
|
148
|
+
for (int iter = 0; iter < count; iter++) {
|
|
149
|
+
// Copy base, XOR salt.
|
|
150
|
+
// Salt occupies bytes 20..51 → lanes 2..6.
|
|
151
|
+
uint64_t A[25];
|
|
152
|
+
for (int i = 0; i < 25; i++) A[i] = base[i];
|
|
153
|
+
|
|
154
|
+
// Lane 2 (bytes 16..23): upper 4 bytes = salt[0..3]
|
|
155
|
+
A[2] ^= (uint64_t)load32_le(salt) << 32;
|
|
156
|
+
// Lane 3 (bytes 24..31): salt[4..11]
|
|
157
|
+
A[3] ^= load64_le(salt + 4);
|
|
158
|
+
// Lane 4 (bytes 32..39): salt[12..19]
|
|
159
|
+
A[4] ^= load64_le(salt + 12);
|
|
160
|
+
// Lane 5 (bytes 40..47): salt[20..27]
|
|
161
|
+
A[5] ^= load64_le(salt + 20);
|
|
162
|
+
// Lane 6 (bytes 48..55): lower 4 bytes = salt[28..31]
|
|
163
|
+
A[6] ^= (uint64_t)load32_le(salt + 28);
|
|
164
|
+
|
|
165
|
+
keccak_f1600(A);
|
|
166
|
+
|
|
167
|
+
// PoW check: first 4 bytes = low 32 bits of lane 0 must be zero.
|
|
168
|
+
if ((uint32_t)A[0] == 0) {
|
|
169
|
+
for (int i = 0; i < 4; i++)
|
|
170
|
+
store64_le(hout + i * 8, A[i]);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Increment big-endian salt.
|
|
175
|
+
for (int i = 31; i >= 0; i--) {
|
|
176
|
+
if (salt[i] < 0xFF) { salt[i]++; break; }
|
|
177
|
+
salt[i] = 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Embedded TIP-1022 WASM keccak256 salt miner (compiled from mine.c).
|
|
2
|
+
//
|
|
3
|
+
// Memory layout at offset 1024:
|
|
4
|
+
// [0..19] address (20 bytes, set by JS)
|
|
5
|
+
// [20..51] salt (32 bytes, set/read by JS, incremented in-place)
|
|
6
|
+
// [52..83] hash out (32 bytes, written on match)
|
|
7
|
+
//
|
|
8
|
+
// Exported: mine(count: i32) -> i32 — returns 1 if found, 0 if not.
|
|
9
|
+
//
|
|
10
|
+
// Generated by: pnpm tsx scripts/build-mine-wasm.ts
|
|
11
|
+
|
|
12
|
+
/** Base64-encoded WASM binary for the TIP-1022 salt miner. */
|
|
13
|
+
export const wasmBase64 =
|
|
14
|
+
'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'
|
|
15
|
+
|
|
16
|
+
/** Byte offset in WASM linear memory where mining I/O begins. */
|
|
17
|
+
export const dataOffset = 1024
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import * as Errors from '../../core/Errors.js'
|
|
2
|
+
|
|
3
|
+
/** Message sent from a mining worker to the main thread. */
|
|
4
|
+
export type Message =
|
|
5
|
+
| { type: 'done' }
|
|
6
|
+
| { type: 'error'; message: string }
|
|
7
|
+
| {
|
|
8
|
+
type: 'found'
|
|
9
|
+
result: {
|
|
10
|
+
/** The 4-byte master identifier. */
|
|
11
|
+
masterId: string
|
|
12
|
+
/** The full 32-byte registration hash. */
|
|
13
|
+
registrationHash: string
|
|
14
|
+
/** The discovered 32-byte salt. */
|
|
15
|
+
salt: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
| { type: 'progress'; attempts: number }
|
|
19
|
+
|
|
20
|
+
/** A platform-agnostic worker pool for parallel salt mining. */
|
|
21
|
+
export type Pool = {
|
|
22
|
+
spawn(
|
|
23
|
+
index: number,
|
|
24
|
+
onMessage: (msg: Message) => void,
|
|
25
|
+
onError: (err: unknown) => void,
|
|
26
|
+
): { postMessage(data: unknown): void; terminate(): void }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let cachedPool: Pool | undefined
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether the current runtime is Node.js.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
const isNode =
|
|
37
|
+
typeof globalThis.process !== 'undefined' &&
|
|
38
|
+
typeof globalThis.process.versions?.node === 'string'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolves the best available worker pool for the current runtime.
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export async function resolve(): Promise<Pool | undefined> {
|
|
46
|
+
if (cachedPool) return cachedPool
|
|
47
|
+
cachedPool = isNode ? await resolveNode() : await resolveBrowser()
|
|
48
|
+
return cachedPool
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a worker pool backed by Node.js `worker_threads`.
|
|
53
|
+
*
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export async function resolveNode(): Promise<Pool | undefined> {
|
|
57
|
+
const { Worker } = await import('node:worker_threads')
|
|
58
|
+
const { wasmBase64 } = await import('./mine.wasm.js')
|
|
59
|
+
const workerSource = getNodeWorkerSource()
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
spawn(index, onMessage, onError) {
|
|
63
|
+
const worker = new Worker(workerSource, {
|
|
64
|
+
eval: true,
|
|
65
|
+
workerData: {
|
|
66
|
+
wasmBase64,
|
|
67
|
+
workerIndex: index,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
worker.on('message', (msg: Message) => onMessage(msg))
|
|
71
|
+
worker.on('error', (err) => onError(err))
|
|
72
|
+
worker.on('exit', (code) => {
|
|
73
|
+
if (code !== 0)
|
|
74
|
+
onError(
|
|
75
|
+
new Errors.BaseError(
|
|
76
|
+
`A salt mining worker exited with code "${code}".`,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
return {
|
|
81
|
+
postMessage(data) {
|
|
82
|
+
worker.postMessage(data)
|
|
83
|
+
},
|
|
84
|
+
terminate() {
|
|
85
|
+
void worker.terminate().catch(() => {})
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a worker pool backed by browser `Worker` with Blob URLs.
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
export async function resolveBrowser(): Promise<Pool | undefined> {
|
|
98
|
+
if (typeof globalThis.Worker === 'undefined') return undefined
|
|
99
|
+
if (typeof globalThis.Blob === 'undefined') return undefined
|
|
100
|
+
|
|
101
|
+
const { wasmBase64 } = await import('./mine.wasm.js')
|
|
102
|
+
const source = getBrowserWorkerSource(wasmBase64)
|
|
103
|
+
const blob = new Blob([source], { type: 'application/javascript' })
|
|
104
|
+
const url = URL.createObjectURL(blob)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
spawn(_index, onMessage, onError) {
|
|
108
|
+
const worker = new globalThis.Worker(url)
|
|
109
|
+
worker.onmessage = (e: MessageEvent<Message>) => onMessage(e.data)
|
|
110
|
+
worker.onerror = (e) => onError(e.error)
|
|
111
|
+
return {
|
|
112
|
+
postMessage(data) {
|
|
113
|
+
worker.postMessage(data)
|
|
114
|
+
},
|
|
115
|
+
terminate() {
|
|
116
|
+
worker.terminate()
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns the inline JavaScript source for a browser Web Worker.
|
|
125
|
+
*
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
function getBrowserWorkerSource(wasmBase64: string): string {
|
|
129
|
+
return `
|
|
130
|
+
'use strict'
|
|
131
|
+
|
|
132
|
+
${getWorkerMineLoop()}
|
|
133
|
+
|
|
134
|
+
var binary = Uint8Array.from(atob(${JSON.stringify(wasmBase64)}), function(c) { return c.charCodeAt(0) })
|
|
135
|
+
|
|
136
|
+
WebAssembly.instantiate(binary).then(function(result) {
|
|
137
|
+
var wasm = result.instance.exports
|
|
138
|
+
var mem = new Uint8Array(wasm.memory.buffer)
|
|
139
|
+
|
|
140
|
+
self.onmessage = function(e) {
|
|
141
|
+
if (e.data.type !== 'start') return
|
|
142
|
+
mineLoop(e.data, wasm, mem, function(msg) { self.postMessage(msg) })
|
|
143
|
+
}
|
|
144
|
+
}).catch(function(error) {
|
|
145
|
+
self.postMessage({ type: 'error', message: error && error.message || String(error) })
|
|
146
|
+
})
|
|
147
|
+
`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Returns the eval'd JavaScript source for a Node.js `worker_threads` worker.
|
|
152
|
+
*
|
|
153
|
+
* @internal
|
|
154
|
+
*/
|
|
155
|
+
function getNodeWorkerSource(): string {
|
|
156
|
+
return `
|
|
157
|
+
'use strict'
|
|
158
|
+
var { parentPort, workerData } = require('node:worker_threads')
|
|
159
|
+
|
|
160
|
+
${getWorkerMineLoop()}
|
|
161
|
+
|
|
162
|
+
async function main() {
|
|
163
|
+
var binary = Buffer.from(workerData.wasmBase64, 'base64')
|
|
164
|
+
var { instance } = await WebAssembly.instantiate(binary)
|
|
165
|
+
var wasm = instance.exports
|
|
166
|
+
var mem = new Uint8Array(wasm.memory.buffer)
|
|
167
|
+
|
|
168
|
+
parentPort.on('message', function(data) {
|
|
169
|
+
if (data.type !== 'start') return
|
|
170
|
+
mineLoop(data, wasm, mem, function(msg) { parentPort.postMessage(msg) })
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main().catch(function(error) {
|
|
175
|
+
parentPort.postMessage({ type: 'error', message: error && error.message || String(error) })
|
|
176
|
+
})
|
|
177
|
+
`
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Returns the shared WASM mining loop source used by both Node and browser workers.
|
|
182
|
+
*
|
|
183
|
+
* @internal
|
|
184
|
+
*/
|
|
185
|
+
function getWorkerMineLoop(): string {
|
|
186
|
+
return `
|
|
187
|
+
var dataOffset = 1024
|
|
188
|
+
|
|
189
|
+
function hexToBytes(hex) {
|
|
190
|
+
var h = hex.startsWith('0x') ? hex.slice(2) : hex
|
|
191
|
+
var bytes = new Uint8Array(h.length / 2)
|
|
192
|
+
for (var i = 0; i < bytes.length; i++)
|
|
193
|
+
bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16)
|
|
194
|
+
return bytes
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function bytesToHex(bytes) {
|
|
198
|
+
var hex = '0x'
|
|
199
|
+
for (var i = 0; i < bytes.length; i++)
|
|
200
|
+
hex += bytes[i].toString(16).padStart(2, '0')
|
|
201
|
+
return hex
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function bigIntToBytes32(value, out, offset) {
|
|
205
|
+
var v = value
|
|
206
|
+
for (var i = 31; i >= 0; i--) {
|
|
207
|
+
out[offset + i] = Number(v & 0xFFn)
|
|
208
|
+
v >>= 8n
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function mineLoop(data, wasm, mem, postMessage) {
|
|
213
|
+
var addressBytes = hexToBytes(data.address)
|
|
214
|
+
var chunkSize = data.chunkSize
|
|
215
|
+
var count = data.count
|
|
216
|
+
var workerCount = data.workerCount
|
|
217
|
+
var workerIndex = data.workerIndex
|
|
218
|
+
var startBigInt = BigInt(data.start)
|
|
219
|
+
|
|
220
|
+
mem.set(addressBytes, dataOffset)
|
|
221
|
+
|
|
222
|
+
for (
|
|
223
|
+
var chunkIdx = workerIndex;
|
|
224
|
+
chunkIdx * chunkSize < count;
|
|
225
|
+
chunkIdx += workerCount
|
|
226
|
+
) {
|
|
227
|
+
var chunkStart = chunkIdx * chunkSize
|
|
228
|
+
var limit = Math.min(chunkSize, count - chunkStart)
|
|
229
|
+
|
|
230
|
+
bigIntToBytes32(startBigInt + BigInt(chunkStart), mem, dataOffset + 20)
|
|
231
|
+
|
|
232
|
+
var found = wasm.mine(limit)
|
|
233
|
+
|
|
234
|
+
if (found) {
|
|
235
|
+
var hashOut = mem.slice(dataOffset + 52, dataOffset + 84)
|
|
236
|
+
var salt = mem.slice(dataOffset + 20, dataOffset + 52)
|
|
237
|
+
postMessage({
|
|
238
|
+
type: 'found',
|
|
239
|
+
result: {
|
|
240
|
+
masterId: bytesToHex(hashOut.subarray(4, 8)),
|
|
241
|
+
registrationHash: bytesToHex(hashOut),
|
|
242
|
+
salt: bytesToHex(salt),
|
|
243
|
+
},
|
|
244
|
+
})
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
postMessage({ type: 'progress', attempts: limit })
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
postMessage({ type: 'done' })
|
|
252
|
+
}
|
|
253
|
+
`
|
|
254
|
+
}
|
package/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = '0.14.
|
|
2
|
+
export const version = '0.14.19'
|