hugin-utils 0.1.2 → 0.1.4
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 +9 -9
- package/index.js +196 -132
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# hugin-utils
|
|
2
|
-
|
|
3
|
-
PoW utils for hugin node messages.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
const { insertNonce, meetsTarget, findShare } = require('hugin-utils');
|
|
9
|
-
```
|
|
1
|
+
# hugin-utils
|
|
2
|
+
|
|
3
|
+
PoW utils for hugin node messages.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { insertNonce, meetsTarget, findShare } = require('hugin-utils');
|
|
9
|
+
```
|
package/index.js
CHANGED
|
@@ -1,132 +1,196 @@
|
|
|
1
|
-
const DEFAULT_NONCE_OFFSET = 39;
|
|
2
|
-
|
|
3
|
-
function readVarint(buffer, offset) {
|
|
4
|
-
let value = 0;
|
|
5
|
-
let shift = 0;
|
|
6
|
-
let bytes = 0;
|
|
7
|
-
while (offset + bytes < buffer.length) {
|
|
8
|
-
const byte = buffer[offset + bytes];
|
|
9
|
-
value |= (byte & 0x7f) << shift;
|
|
10
|
-
bytes += 1;
|
|
11
|
-
if ((byte & 0x80) === 0) {
|
|
12
|
-
return { value, bytes };
|
|
13
|
-
}
|
|
14
|
-
shift += 7;
|
|
15
|
-
if (shift > 63) return null;
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getNonceOffsetFromBuffer(blobBuffer) {
|
|
21
|
-
try {
|
|
22
|
-
let offset = 0;
|
|
23
|
-
const major = readVarint(blobBuffer, offset);
|
|
24
|
-
if (!major) return DEFAULT_NONCE_OFFSET;
|
|
25
|
-
offset += major.bytes;
|
|
26
|
-
const minor = readVarint(blobBuffer, offset);
|
|
27
|
-
if (!minor) return DEFAULT_NONCE_OFFSET;
|
|
28
|
-
offset += minor.bytes;
|
|
29
|
-
const timestamp = readVarint(blobBuffer, offset);
|
|
30
|
-
if (!timestamp) return DEFAULT_NONCE_OFFSET;
|
|
31
|
-
offset += timestamp.bytes;
|
|
32
|
-
offset += 32;
|
|
33
|
-
return offset;
|
|
34
|
-
} catch (e) {
|
|
35
|
-
return DEFAULT_NONCE_OFFSET;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getNonceOffset(blobHex) {
|
|
40
|
-
const blobBuffer = Buffer.from(blobHex, 'hex');
|
|
41
|
-
return getNonceOffsetFromBuffer(blobBuffer);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function insertNonce(blobHex, nonceHex) {
|
|
45
|
-
const blobBuffer = Buffer.from(blobHex, 'hex');
|
|
46
|
-
const nonceBuffer = Buffer.from(nonceHex, 'hex');
|
|
47
|
-
const offset = getNonceOffsetFromBuffer(blobBuffer);
|
|
48
|
-
nonceBuffer.copy(blobBuffer, offset);
|
|
49
|
-
return { blobHex: blobBuffer.toString('hex'), offset };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
1
|
+
const DEFAULT_NONCE_OFFSET = 39;
|
|
2
|
+
|
|
3
|
+
function readVarint(buffer, offset) {
|
|
4
|
+
let value = 0;
|
|
5
|
+
let shift = 0;
|
|
6
|
+
let bytes = 0;
|
|
7
|
+
while (offset + bytes < buffer.length) {
|
|
8
|
+
const byte = buffer[offset + bytes];
|
|
9
|
+
value |= (byte & 0x7f) << shift;
|
|
10
|
+
bytes += 1;
|
|
11
|
+
if ((byte & 0x80) === 0) {
|
|
12
|
+
return { value, bytes };
|
|
13
|
+
}
|
|
14
|
+
shift += 7;
|
|
15
|
+
if (shift > 63) return null;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getNonceOffsetFromBuffer(blobBuffer) {
|
|
21
|
+
try {
|
|
22
|
+
let offset = 0;
|
|
23
|
+
const major = readVarint(blobBuffer, offset);
|
|
24
|
+
if (!major) return DEFAULT_NONCE_OFFSET;
|
|
25
|
+
offset += major.bytes;
|
|
26
|
+
const minor = readVarint(blobBuffer, offset);
|
|
27
|
+
if (!minor) return DEFAULT_NONCE_OFFSET;
|
|
28
|
+
offset += minor.bytes;
|
|
29
|
+
const timestamp = readVarint(blobBuffer, offset);
|
|
30
|
+
if (!timestamp) return DEFAULT_NONCE_OFFSET;
|
|
31
|
+
offset += timestamp.bytes;
|
|
32
|
+
offset += 32;
|
|
33
|
+
return offset;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return DEFAULT_NONCE_OFFSET;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getNonceOffset(blobHex) {
|
|
40
|
+
const blobBuffer = Buffer.from(blobHex, 'hex');
|
|
41
|
+
return getNonceOffsetFromBuffer(blobBuffer);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function insertNonce(blobHex, nonceHex) {
|
|
45
|
+
const blobBuffer = Buffer.from(blobHex, 'hex');
|
|
46
|
+
const nonceBuffer = Buffer.from(nonceHex, 'hex');
|
|
47
|
+
const offset = getNonceOffsetFromBuffer(blobBuffer);
|
|
48
|
+
nonceBuffer.copy(blobBuffer, offset);
|
|
49
|
+
return { blobHex: blobBuffer.toString('hex'), offset };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function extractPrevIdFromBlob(blobHex) {
|
|
53
|
+
try {
|
|
54
|
+
const blobBuffer = Buffer.from(blobHex, 'hex');
|
|
55
|
+
let offset = 0;
|
|
56
|
+
const major = readVarint(blobBuffer, offset);
|
|
57
|
+
if (!major) return null;
|
|
58
|
+
offset += major.bytes;
|
|
59
|
+
const minor = readVarint(blobBuffer, offset);
|
|
60
|
+
if (!minor) return null;
|
|
61
|
+
offset += minor.bytes;
|
|
62
|
+
const timestamp = readVarint(blobBuffer, offset);
|
|
63
|
+
if (!timestamp) return null;
|
|
64
|
+
offset += timestamp.bytes;
|
|
65
|
+
if (offset + 32 > blobBuffer.length) return null;
|
|
66
|
+
return blobBuffer.subarray(offset, offset + 32).toString('hex');
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function nonceToHexLE(nonce) {
|
|
73
|
+
const buf = Buffer.alloc(4);
|
|
74
|
+
buf.writeUInt32LE(nonce >>> 0, 0);
|
|
75
|
+
return buf.toString('hex');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseTarget(targetHex) {
|
|
79
|
+
if (!targetHex) return null;
|
|
80
|
+
const targetBuffer = Buffer.from(targetHex, 'hex');
|
|
81
|
+
if (targetBuffer.length === 4) {
|
|
82
|
+
const raw = targetBuffer.readUInt32LE(0);
|
|
83
|
+
if (raw === 0) return null;
|
|
84
|
+
const numerator = 0xFFFFFFFFFFFFFFFFn;
|
|
85
|
+
const denom = 0xFFFFFFFFn / BigInt(raw);
|
|
86
|
+
if (denom === 0n) return null;
|
|
87
|
+
return numerator / denom;
|
|
88
|
+
}
|
|
89
|
+
if (targetBuffer.length === 8) {
|
|
90
|
+
return targetBuffer.readBigUInt64LE(0);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function meetsTarget(hashHex, targetHex) {
|
|
96
|
+
const target = parseTarget(targetHex);
|
|
97
|
+
if (target === null) return true;
|
|
98
|
+
const hash = Buffer.from(hashHex, 'hex');
|
|
99
|
+
if (hash.length < 32) return false;
|
|
100
|
+
const hashTail = hash.readBigUInt64LE(24);
|
|
101
|
+
return hashTail <= target;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function minTargetHex(a, b) {
|
|
105
|
+
const aa = parseTarget(a);
|
|
106
|
+
const bb = parseTarget(b);
|
|
107
|
+
if (aa === null) return b;
|
|
108
|
+
if (bb === null) return a;
|
|
109
|
+
return aa <= bb ? a : b;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function nonceTagFromMessageHash(messageHash, bits) {
|
|
113
|
+
try {
|
|
114
|
+
const b = typeof bits === 'number' ? bits : 0;
|
|
115
|
+
if (b <= 0 || b > 16) return 0;
|
|
116
|
+
const mask = (1 << b) - 1;
|
|
117
|
+
const digest0 = require('crypto')
|
|
118
|
+
.createHash('sha256')
|
|
119
|
+
.update(String(messageHash))
|
|
120
|
+
.digest()[0];
|
|
121
|
+
return digest0 & mask;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function nonceMatchesTag(nonceHex, tagValue, bits) {
|
|
128
|
+
if (typeof nonceHex !== 'string' || nonceHex.length !== 8) return false;
|
|
129
|
+
const b = typeof bits === 'number' ? bits : 0;
|
|
130
|
+
if (b <= 0) return true;
|
|
131
|
+
const mask = (1 << b) - 1;
|
|
132
|
+
const nonce = parseInt(nonceHex, 16) >>> 0;
|
|
133
|
+
return (nonce & mask) === (tagValue & mask);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function findShare({
|
|
137
|
+
job,
|
|
138
|
+
startNonce,
|
|
139
|
+
hashFn,
|
|
140
|
+
log,
|
|
141
|
+
hashesPerSecond = 500,
|
|
142
|
+
timeBudgetMs = 1000,
|
|
143
|
+
yieldEvery = 200,
|
|
144
|
+
logEvery = 100,
|
|
145
|
+
nonceTagBits = 0,
|
|
146
|
+
nonceTagValue = 0
|
|
147
|
+
}) {
|
|
148
|
+
const maxAttempts = Math.max(1, Math.floor((hashesPerSecond * timeBudgetMs) / 1000));
|
|
149
|
+
let nonce = startNonce;
|
|
150
|
+
const start = Date.now();
|
|
151
|
+
const b = typeof nonceTagBits === 'number' ? nonceTagBits : 0;
|
|
152
|
+
const mask = b > 0 ? ((1 << b) - 1) : 0;
|
|
153
|
+
|
|
154
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
155
|
+
if (b > 0 && ((nonce >>> 0) & mask) !== (nonceTagValue & mask)) {
|
|
156
|
+
nonce++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const nonceHex = nonceToHexLE(nonce);
|
|
160
|
+
const { blobHex, offset } = insertNonce(job.blob, nonceHex);
|
|
161
|
+
if (log) log('nonce_offset', { jobId: job.job_id, offset });
|
|
162
|
+
const result = await hashFn(blobHex);
|
|
163
|
+
|
|
164
|
+
if (meetsTarget(result, job.target)) {
|
|
165
|
+
if (log) log('pow_share_found', { jobId: job.job_id, nonce: nonceHex, attempt });
|
|
166
|
+
return { job_id: job.job_id, nonce: nonceHex, result };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
nonce++;
|
|
170
|
+
if (attempt > 0 && attempt % logEvery === 0) {
|
|
171
|
+
if (log) log('pow_progress', { jobId: job.job_id, attempt, nonce: nonceHex });
|
|
172
|
+
}
|
|
173
|
+
if (attempt > 0 && attempt % yieldEvery === 0) {
|
|
174
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
175
|
+
}
|
|
176
|
+
if (Date.now() - start >= timeBudgetMs) {
|
|
177
|
+
if (log) log('pow_time_budget', { jobId: job.job_id, attempt, elapsedMs: Date.now() - start });
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (log) log('pow_share_exhausted', { jobId: job.job_id, attempts: maxAttempts });
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
readVarint,
|
|
188
|
+
getNonceOffset,
|
|
189
|
+
insertNonce,
|
|
190
|
+
meetsTarget,
|
|
191
|
+
minTargetHex,
|
|
192
|
+
extractPrevIdFromBlob,
|
|
193
|
+
nonceTagFromMessageHash,
|
|
194
|
+
nonceMatchesTag,
|
|
195
|
+
findShare
|
|
196
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "hugin-utils",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "PoW utils for hugin node messages",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "n9lsjr"
|
|
8
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "hugin-utils",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "PoW utils for hugin node messages",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "n9lsjr"
|
|
8
|
+
}
|