@totemsdk/txpow 0.1.3 → 0.1.5
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 +21 -0
- package/README.md +3 -3
- package/dist/calibrate.js +8 -4
- package/dist/constants.js +8 -5
- package/dist/difficulty.js +6 -3
- package/dist/index.js +27 -7
- package/dist/magic.js +15 -12
- package/dist/mine.js +19 -15
- package/dist/serialization.js +34 -28
- package/dist/verify.js +14 -10
- package/package.json +26 -5
- package/src/__tests__/serialization.test.ts +0 -347
- package/src/calibrate.ts +0 -86
- package/src/constants.ts +0 -49
- package/src/difficulty.ts +0 -69
- package/src/index.ts +0 -56
- package/src/magic.ts +0 -37
- package/src/mine.ts +0 -212
- package/src/serialization.ts +0 -236
- package/src/verify.ts +0 -96
- package/src/wasm/miner.c +0 -165
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Totem SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -71,6 +71,6 @@ console.log(`Expected mining time: ${estimatedMs}ms`);
|
|
|
71
71
|
|
|
72
72
|
## See also
|
|
73
73
|
|
|
74
|
-
- [`@totemsdk/tx-builder`](
|
|
75
|
-
- [`@totemsdk/node`](
|
|
76
|
-
- [`@totemsdk/chain-provider`](
|
|
74
|
+
- [`@totemsdk/tx-builder`](https://www.npmjs.com/package/@totemsdk/tx-builder) — construct `txBodyBytes` before mining
|
|
75
|
+
- [`@totemsdk/node`](https://www.npmjs.com/package/@totemsdk/node) — Node.js wallet that uses txpow internally
|
|
76
|
+
- [`@totemsdk/chain-provider`](https://www.npmjs.com/package/@totemsdk/chain-provider) — `broadcastTxPoW` endpoint
|
package/dist/calibrate.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* calibrate.ts — Hash rate measurement and mining cost estimation.
|
|
3
4
|
*
|
|
@@ -8,21 +9,24 @@
|
|
|
8
9
|
* estimateMiningCost() converts a difficulty target + hash rate into an
|
|
9
10
|
* expected time and confidence label.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.calibrateHashRate = calibrateHashRate;
|
|
14
|
+
exports.estimateMiningCost = estimateMiningCost;
|
|
15
|
+
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
12
16
|
/**
|
|
13
17
|
* Run 100K trial SHA3-256 hashes and return the measured hash rate (hashes/sec).
|
|
14
18
|
*
|
|
15
19
|
* Call once per session and cache the result — the cost is ~50ms on a
|
|
16
20
|
* typical desktop and ~300ms on a mid-range mobile.
|
|
17
21
|
*/
|
|
18
|
-
|
|
22
|
+
async function calibrateHashRate() {
|
|
19
23
|
const TRIAL_COUNT = 100000;
|
|
20
24
|
const data = new Uint8Array(150).fill(0xab);
|
|
21
25
|
const start = typeof performance !== 'undefined'
|
|
22
26
|
? performance.now()
|
|
23
27
|
: Date.now();
|
|
24
28
|
for (let i = 0; i < TRIAL_COUNT; i++) {
|
|
25
|
-
sha3_256(data);
|
|
29
|
+
(0, sha3_js_1.sha3_256)(data);
|
|
26
30
|
}
|
|
27
31
|
const elapsed = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - start;
|
|
28
32
|
return Math.round(TRIAL_COUNT / (elapsed / 1000));
|
|
@@ -39,7 +43,7 @@ export async function calibrateHashRate() {
|
|
|
39
43
|
* normal 2 – 15 s
|
|
40
44
|
* slow > 15 s
|
|
41
45
|
*/
|
|
42
|
-
|
|
46
|
+
function estimateMiningCost(txnDifficulty, hashRatePerSec) {
|
|
43
47
|
if (hashRatePerSec <= 0) {
|
|
44
48
|
throw new Error('hashRatePerSec must be positive');
|
|
45
49
|
}
|
package/dist/constants.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* constants.ts — TxPoW-level constants matching Minima Java protocol values.
|
|
3
4
|
*
|
|
@@ -10,9 +11,11 @@
|
|
|
10
11
|
* MAIN_NET_CHAIN_ID: 1-byte [0x00] — Java MiniData("0x00") = MAIN_NET chain ID.
|
|
11
12
|
* CASCADE_LEVELS: 32 — number of super-parent slots in a fresh TxPoW header.
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.MAIN_NET_CHAIN_ID = exports.TX_POW_MIN_DIFFICULTY = exports.MAX_HASH = exports.ZERO_HASH = exports.CASCADE_LEVELS = void 0;
|
|
16
|
+
exports.CASCADE_LEVELS = 32;
|
|
17
|
+
exports.ZERO_HASH = new Uint8Array(32);
|
|
18
|
+
exports.MAX_HASH = (() => {
|
|
16
19
|
const b = new Uint8Array(32);
|
|
17
20
|
b.fill(0xff);
|
|
18
21
|
return b;
|
|
@@ -27,7 +30,7 @@ export const MAX_HASH = (() => {
|
|
|
27
30
|
* Computed: (2n**256n - 1n) / 1_000_000n
|
|
28
31
|
* Hex: 0x000010C6F7A0B5ED8538AACDD46595F0C7AC73E0E9DBF12F70000000000000000
|
|
29
32
|
*/
|
|
30
|
-
|
|
33
|
+
exports.TX_POW_MIN_DIFFICULTY = (() => {
|
|
31
34
|
const maxHashBigInt = (2n ** 256n) - 1n;
|
|
32
35
|
const minDiffBigInt = maxHashBigInt / 1000000n;
|
|
33
36
|
let hex = minDiffBigInt.toString(16);
|
|
@@ -41,4 +44,4 @@ export const TX_POW_MIN_DIFFICULTY = (() => {
|
|
|
41
44
|
}
|
|
42
45
|
return bytes;
|
|
43
46
|
})();
|
|
44
|
-
|
|
47
|
+
exports.MAIN_NET_CHAIN_ID = new Uint8Array([0x00]);
|
package/dist/difficulty.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* difficulty.ts — Fetch and manage the TxPoW difficulty target.
|
|
3
4
|
*
|
|
@@ -5,7 +6,9 @@
|
|
|
5
6
|
* On any error (timeout, network failure, invalid response) we fall back to
|
|
6
7
|
* the hardcoded TX_POW_MIN_DIFFICULTY constant, which is the protocol floor.
|
|
7
8
|
*/
|
|
8
|
-
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.fetchTxPowTarget = fetchTxPowTarget;
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
9
12
|
function hexToBytes(hex) {
|
|
10
13
|
const h = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
11
14
|
const out = new Uint8Array(h.length / 2);
|
|
@@ -23,7 +26,7 @@ function hexToBytes(hex) {
|
|
|
23
26
|
* @param axiaBaseUrl Base URL of the Axia API, e.g. "https://api.axia.to"
|
|
24
27
|
* @param timeoutMs Request timeout in ms (default: 3000)
|
|
25
28
|
*/
|
|
26
|
-
|
|
29
|
+
async function fetchTxPowTarget(axiaBaseUrl, timeoutMs = 3000) {
|
|
27
30
|
try {
|
|
28
31
|
const base = axiaBaseUrl.replace(/\/$/, '');
|
|
29
32
|
const url = `${base}/v1/wallet/txpow-params`;
|
|
@@ -50,6 +53,6 @@ export async function fetchTxPowTarget(axiaBaseUrl, timeoutMs = 3000) {
|
|
|
50
53
|
return hexToBytes(clean);
|
|
51
54
|
}
|
|
52
55
|
catch {
|
|
53
|
-
return new Uint8Array(TX_POW_MIN_DIFFICULTY);
|
|
56
|
+
return new Uint8Array(constants_js_1.TX_POW_MIN_DIFFICULTY);
|
|
54
57
|
}
|
|
55
58
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* @totemsdk/txpow
|
|
3
4
|
*
|
|
@@ -17,10 +18,29 @@
|
|
|
17
18
|
* const check = verifyProofOfWork(txpowId, mTxnDifficulty);
|
|
18
19
|
* if (!check.valid) drop(check.reason);
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.estimateMiningCost = exports.calibrateHashRate = exports.verifyTxPoWWork = exports.verifyProofOfWork = exports.fetchTxPowTarget = exports.isLessThan = exports.mineTxPoW = exports.serializeMagic = exports.MAIN_NET_CHAIN_ID = exports.CASCADE_LEVELS = exports.TX_POW_MIN_DIFFICULTY = exports.ZERO_HASH = exports.MAX_HASH = exports.computeTxPoWId = exports.serializeTxPoW = exports.serializeTxBody = exports.serializeTxHeader = void 0;
|
|
23
|
+
var serialization_js_1 = require("./serialization.js");
|
|
24
|
+
Object.defineProperty(exports, "serializeTxHeader", { enumerable: true, get: function () { return serialization_js_1.serializeTxHeader; } });
|
|
25
|
+
Object.defineProperty(exports, "serializeTxBody", { enumerable: true, get: function () { return serialization_js_1.serializeTxBody; } });
|
|
26
|
+
Object.defineProperty(exports, "serializeTxPoW", { enumerable: true, get: function () { return serialization_js_1.serializeTxPoW; } });
|
|
27
|
+
Object.defineProperty(exports, "computeTxPoWId", { enumerable: true, get: function () { return serialization_js_1.computeTxPoWId; } });
|
|
28
|
+
var constants_js_1 = require("./constants.js");
|
|
29
|
+
Object.defineProperty(exports, "MAX_HASH", { enumerable: true, get: function () { return constants_js_1.MAX_HASH; } });
|
|
30
|
+
Object.defineProperty(exports, "ZERO_HASH", { enumerable: true, get: function () { return constants_js_1.ZERO_HASH; } });
|
|
31
|
+
Object.defineProperty(exports, "TX_POW_MIN_DIFFICULTY", { enumerable: true, get: function () { return constants_js_1.TX_POW_MIN_DIFFICULTY; } });
|
|
32
|
+
Object.defineProperty(exports, "CASCADE_LEVELS", { enumerable: true, get: function () { return constants_js_1.CASCADE_LEVELS; } });
|
|
33
|
+
Object.defineProperty(exports, "MAIN_NET_CHAIN_ID", { enumerable: true, get: function () { return constants_js_1.MAIN_NET_CHAIN_ID; } });
|
|
34
|
+
var magic_js_1 = require("./magic.js");
|
|
35
|
+
Object.defineProperty(exports, "serializeMagic", { enumerable: true, get: function () { return magic_js_1.serializeMagic; } });
|
|
36
|
+
var mine_js_1 = require("./mine.js");
|
|
37
|
+
Object.defineProperty(exports, "mineTxPoW", { enumerable: true, get: function () { return mine_js_1.mineTxPoW; } });
|
|
38
|
+
Object.defineProperty(exports, "isLessThan", { enumerable: true, get: function () { return mine_js_1.isLessThan; } });
|
|
39
|
+
var difficulty_js_1 = require("./difficulty.js");
|
|
40
|
+
Object.defineProperty(exports, "fetchTxPowTarget", { enumerable: true, get: function () { return difficulty_js_1.fetchTxPowTarget; } });
|
|
41
|
+
var verify_js_1 = require("./verify.js");
|
|
42
|
+
Object.defineProperty(exports, "verifyProofOfWork", { enumerable: true, get: function () { return verify_js_1.verifyProofOfWork; } });
|
|
43
|
+
Object.defineProperty(exports, "verifyTxPoWWork", { enumerable: true, get: function () { return verify_js_1.verifyTxPoWWork; } });
|
|
44
|
+
var calibrate_js_1 = require("./calibrate.js");
|
|
45
|
+
Object.defineProperty(exports, "calibrateHashRate", { enumerable: true, get: function () { return calibrate_js_1.calibrateHashRate; } });
|
|
46
|
+
Object.defineProperty(exports, "estimateMiningCost", { enumerable: true, get: function () { return calibrate_js_1.estimateMiningCost; } });
|
package/dist/magic.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* magic.ts — Serialize the Minima Magic struct.
|
|
3
4
|
*
|
|
@@ -16,17 +17,19 @@
|
|
|
16
17
|
* maxTxnPerBlock = 256
|
|
17
18
|
* minTxPoWWork = MAX_HASH (all 0xFF, 32 bytes) — placeholder in fresh TxPoW
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.serializeMagic = serializeMagic;
|
|
22
|
+
const core_1 = require("@totemsdk/core");
|
|
23
|
+
const constants_js_1 = require("./constants.js");
|
|
24
|
+
function serializeMagic() {
|
|
22
25
|
const parts = [];
|
|
23
|
-
parts.push(writeMiniNumber(65536n));
|
|
24
|
-
parts.push(writeMiniNumber(1024n));
|
|
25
|
-
parts.push(writeMiniNumber(256n));
|
|
26
|
-
parts.push(writeMiniData(MAX_HASH));
|
|
27
|
-
parts.push(writeMiniNumber(65536n));
|
|
28
|
-
parts.push(writeMiniNumber(1024n));
|
|
29
|
-
parts.push(writeMiniNumber(256n));
|
|
30
|
-
parts.push(writeMiniData(MAX_HASH));
|
|
31
|
-
return concat(...parts);
|
|
26
|
+
parts.push((0, core_1.writeMiniNumber)(65536n));
|
|
27
|
+
parts.push((0, core_1.writeMiniNumber)(1024n));
|
|
28
|
+
parts.push((0, core_1.writeMiniNumber)(256n));
|
|
29
|
+
parts.push((0, core_1.writeMiniData)(constants_js_1.MAX_HASH));
|
|
30
|
+
parts.push((0, core_1.writeMiniNumber)(65536n));
|
|
31
|
+
parts.push((0, core_1.writeMiniNumber)(1024n));
|
|
32
|
+
parts.push((0, core_1.writeMiniNumber)(256n));
|
|
33
|
+
parts.push((0, core_1.writeMiniData)(constants_js_1.MAX_HASH));
|
|
34
|
+
return (0, core_1.concat)(...parts);
|
|
32
35
|
}
|
package/dist/mine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* mine.ts — Local TxPoW proof-of-work mining loop.
|
|
3
4
|
*
|
|
@@ -14,10 +15,13 @@
|
|
|
14
15
|
* re-allocate a new header buffer with the wider nonce field.
|
|
15
16
|
* 5. Between chunks we yield to the event loop so the caller thread isn't blocked.
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.isLessThan = isLessThan;
|
|
20
|
+
exports.mineTxPoW = mineTxPoW;
|
|
21
|
+
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
22
|
+
const core_1 = require("@totemsdk/core");
|
|
23
|
+
const magic_js_1 = require("./magic.js");
|
|
24
|
+
const constants_js_1 = require("./constants.js");
|
|
21
25
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
26
|
// Internal helpers
|
|
23
27
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -25,7 +29,7 @@ import { MAX_HASH, ZERO_HASH, CASCADE_LEVELS, MAIN_NET_CHAIN_ID } from './consta
|
|
|
25
29
|
* Big-endian 256-bit comparison: true if a < b.
|
|
26
30
|
* Used to check: txpowId < mTxnDifficulty.
|
|
27
31
|
*/
|
|
28
|
-
|
|
32
|
+
function isLessThan(a, b) {
|
|
29
33
|
for (let i = 0; i < 32; i++) {
|
|
30
34
|
if (a[i] < b[i])
|
|
31
35
|
return true;
|
|
@@ -44,7 +48,7 @@ export function isLessThan(a, b) {
|
|
|
44
48
|
* k=3: 2^23 = 8388608
|
|
45
49
|
*/
|
|
46
50
|
function nextNonceBoundary(nonce) {
|
|
47
|
-
const nonceValueLen = writeMiniNumber(nonce).length - 2;
|
|
51
|
+
const nonceValueLen = (0, core_1.writeMiniNumber)(nonce).length - 2;
|
|
48
52
|
return 2n ** BigInt(8 * nonceValueLen - 1);
|
|
49
53
|
}
|
|
50
54
|
/**
|
|
@@ -53,7 +57,7 @@ function nextNonceBoundary(nonce) {
|
|
|
53
57
|
* This is computed once at the start of a mine and never changes during it.
|
|
54
58
|
*/
|
|
55
59
|
function buildHeaderTail(txBodyHash, timeMilli) {
|
|
56
|
-
return concat(writeMiniData(MAIN_NET_CHAIN_ID), writeMiniNumber(timeMilli), writeMiniNumber(0n), writeMiniData(MAX_HASH), new Uint8Array([CASCADE_LEVELS]), writeHashToStream(ZERO_HASH), writeHashToStream(ZERO_HASH), writeMiniNumber(0n), serializeMagic(), writeHashToStream(ZERO_HASH), writeHashToStream(txBodyHash));
|
|
60
|
+
return (0, core_1.concat)((0, core_1.writeMiniData)(constants_js_1.MAIN_NET_CHAIN_ID), (0, core_1.writeMiniNumber)(timeMilli), (0, core_1.writeMiniNumber)(0n), (0, core_1.writeMiniData)(constants_js_1.MAX_HASH), new Uint8Array([constants_js_1.CASCADE_LEVELS]), (0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_1.writeMiniNumber)(0n), (0, magic_js_1.serializeMagic)(), (0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_1.writeHashToStream)(txBodyHash));
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* Synchronously mine a chunk of nonces within a fixed encoding boundary.
|
|
@@ -69,7 +73,7 @@ function mineChunk(headerBuf, nonceValueLen, target, startNonce, endNonce) {
|
|
|
69
73
|
headerBuf[nonceValueOffset + i] = Number(n & 0xffn);
|
|
70
74
|
n >>= 8n;
|
|
71
75
|
}
|
|
72
|
-
const hash = sha3_256(headerBuf);
|
|
76
|
+
const hash = (0, sha3_js_1.sha3_256)(headerBuf);
|
|
73
77
|
if (isLessThan(hash, target))
|
|
74
78
|
return nonce;
|
|
75
79
|
}
|
|
@@ -88,13 +92,13 @@ function mineChunk(headerBuf, nonceValueLen, target, startNonce, endNonce) {
|
|
|
88
92
|
* at block level by TxPoWChecker.checkTxPoWSimple().
|
|
89
93
|
* @param options Chunk size, max iterations, abort signal.
|
|
90
94
|
*/
|
|
91
|
-
|
|
95
|
+
async function mineTxPoW(txBodyBytes, txnDifficulty, options) {
|
|
92
96
|
const chunkSize = BigInt(options?.chunkSize ?? 10000);
|
|
93
97
|
const maxIterations = options?.maxIterations !== undefined
|
|
94
98
|
? BigInt(options.maxIterations)
|
|
95
99
|
: undefined;
|
|
96
100
|
const signal = options?.signal;
|
|
97
|
-
const bodyHash = sha3_256(txBodyBytes);
|
|
101
|
+
const bodyHash = (0, sha3_js_1.sha3_256)(txBodyBytes);
|
|
98
102
|
const timeMilli = BigInt(Date.now());
|
|
99
103
|
const headerTail = buildHeaderTail(bodyHash, timeMilli);
|
|
100
104
|
const startTime = Date.now();
|
|
@@ -109,14 +113,14 @@ export async function mineTxPoW(txBodyBytes, txnDifficulty, options) {
|
|
|
109
113
|
}
|
|
110
114
|
const boundary = nextNonceBoundary(nonce);
|
|
111
115
|
const chunkEnd = nonce + chunkSize < boundary ? nonce + chunkSize : boundary;
|
|
112
|
-
const nonceBytes = writeMiniNumber(nonce);
|
|
116
|
+
const nonceBytes = (0, core_1.writeMiniNumber)(nonce);
|
|
113
117
|
const nonceValueLen = nonceBytes.length - 2;
|
|
114
|
-
const headerBuf = concat(nonceBytes, headerTail);
|
|
118
|
+
const headerBuf = (0, core_1.concat)(nonceBytes, headerTail);
|
|
115
119
|
const found = mineChunk(headerBuf, nonceValueLen, txnDifficulty, nonce, chunkEnd);
|
|
116
120
|
if (found !== null) {
|
|
117
|
-
const finalNonceBytes = writeMiniNumber(found);
|
|
118
|
-
const finalHeader = concat(finalNonceBytes, headerTail);
|
|
119
|
-
const txpowId = sha3_256(finalHeader);
|
|
121
|
+
const finalNonceBytes = (0, core_1.writeMiniNumber)(found);
|
|
122
|
+
const finalHeader = (0, core_1.concat)(finalNonceBytes, headerTail);
|
|
123
|
+
const txpowId = (0, sha3_js_1.sha3_256)(finalHeader);
|
|
120
124
|
return {
|
|
121
125
|
minedHeaderBytes: finalHeader,
|
|
122
126
|
txpowId,
|
package/dist/serialization.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* serialization.ts — TxPoW envelope serialization.
|
|
3
4
|
*
|
|
@@ -50,40 +51,45 @@
|
|
|
50
51
|
* TxPoW ID:
|
|
51
52
|
* SHA3-256( TxHeader bytes ) — Java: Crypto.getInstance().hashObject(mHeader)
|
|
52
53
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
60
|
+
const core_1 = require("@totemsdk/core");
|
|
61
|
+
const magic_js_1 = require("./magic.js");
|
|
62
|
+
const constants_js_1 = require("./constants.js");
|
|
57
63
|
/**
|
|
58
64
|
* Serialize a TxHeader per Minima's TxHeader.writeDataStream().
|
|
59
65
|
*
|
|
60
66
|
* @param txBodyHash SHA3-256 of the serialized TxBody (32 bytes)
|
|
61
67
|
* @param options Optional nonce and timeMilli overrides
|
|
62
68
|
*/
|
|
63
|
-
|
|
69
|
+
function serializeTxHeader(txBodyHash, options) {
|
|
64
70
|
const nonce = options?.nonce ?? 0n;
|
|
65
71
|
const timeMilli = options?.timeMilli ?? BigInt(Date.now());
|
|
66
72
|
const parts = [];
|
|
67
|
-
parts.push(writeMiniNumber(nonce));
|
|
68
|
-
parts.push(writeMiniData(MAIN_NET_CHAIN_ID));
|
|
69
|
-
parts.push(writeMiniNumber(timeMilli));
|
|
70
|
-
parts.push(writeMiniNumber(0n));
|
|
71
|
-
parts.push(writeMiniData(MAX_HASH));
|
|
72
|
-
parts.push(new Uint8Array([CASCADE_LEVELS]));
|
|
73
|
-
parts.push(writeHashToStream(ZERO_HASH));
|
|
74
|
-
parts.push(writeHashToStream(ZERO_HASH));
|
|
75
|
-
parts.push(writeMiniNumber(0n));
|
|
76
|
-
parts.push(serializeMagic());
|
|
77
|
-
parts.push(writeHashToStream(ZERO_HASH));
|
|
78
|
-
parts.push(writeHashToStream(txBodyHash));
|
|
79
|
-
return concat(...parts);
|
|
73
|
+
parts.push((0, core_1.writeMiniNumber)(nonce));
|
|
74
|
+
parts.push((0, core_1.writeMiniData)(constants_js_1.MAIN_NET_CHAIN_ID));
|
|
75
|
+
parts.push((0, core_1.writeMiniNumber)(timeMilli));
|
|
76
|
+
parts.push((0, core_1.writeMiniNumber)(0n));
|
|
77
|
+
parts.push((0, core_1.writeMiniData)(constants_js_1.MAX_HASH));
|
|
78
|
+
parts.push(new Uint8Array([constants_js_1.CASCADE_LEVELS]));
|
|
79
|
+
parts.push((0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
80
|
+
parts.push((0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
81
|
+
parts.push((0, core_1.writeMiniNumber)(0n));
|
|
82
|
+
parts.push((0, magic_js_1.serializeMagic)());
|
|
83
|
+
parts.push((0, core_1.writeHashToStream)(constants_js_1.ZERO_HASH));
|
|
84
|
+
parts.push((0, core_1.writeHashToStream)(txBodyHash));
|
|
85
|
+
return (0, core_1.concat)(...parts);
|
|
80
86
|
}
|
|
81
87
|
/**
|
|
82
88
|
* Compute TxPoW ID = SHA3-256(TxHeader bytes).
|
|
83
89
|
* Matches Java: Crypto.getInstance().hashObject(mHeader) via SHA3Digest(256).
|
|
84
90
|
*/
|
|
85
|
-
|
|
86
|
-
return sha3_256(headerBytes);
|
|
91
|
+
function computeTxPoWId(headerBytes) {
|
|
92
|
+
return (0, sha3_js_1.sha3_256)(headerBytes);
|
|
87
93
|
}
|
|
88
94
|
/**
|
|
89
95
|
* Build the empty burn transaction bytes.
|
|
@@ -97,7 +103,7 @@ export function computeTxPoWId(headerBytes) {
|
|
|
97
103
|
* writeHashToStream([0x00]) linkHash = ZERO_TXPOWID (1 byte)
|
|
98
104
|
*/
|
|
99
105
|
function buildEmptyBurnTxBytes() {
|
|
100
|
-
return concat(writeMiniNumber(0n), writeMiniNumber(0n), writeMiniNumber(0n), writeHashToStream(new Uint8Array([0x00])));
|
|
106
|
+
return (0, core_1.concat)((0, core_1.writeMiniNumber)(0n), (0, core_1.writeMiniNumber)(0n), (0, core_1.writeMiniNumber)(0n), (0, core_1.writeHashToStream)(new Uint8Array([0x00])));
|
|
101
107
|
}
|
|
102
108
|
/**
|
|
103
109
|
* Build the empty burn witness bytes.
|
|
@@ -108,7 +114,7 @@ function buildEmptyBurnTxBytes() {
|
|
|
108
114
|
* writeMiniNumber(0n) scriptproof count
|
|
109
115
|
*/
|
|
110
116
|
function buildEmptyBurnWitnessBytes() {
|
|
111
|
-
return concat(writeMiniNumber(0n), writeMiniNumber(0n), writeMiniNumber(0n));
|
|
117
|
+
return (0, core_1.concat)((0, core_1.writeMiniNumber)(0n), (0, core_1.writeMiniNumber)(0n), (0, core_1.writeMiniNumber)(0n));
|
|
112
118
|
}
|
|
113
119
|
/**
|
|
114
120
|
* Serialize a TxBody per Minima's TxBody.writeDataStream().
|
|
@@ -117,8 +123,8 @@ function buildEmptyBurnWitnessBytes() {
|
|
|
117
123
|
* @param witnessBytes Pre-serialized Witness bytes (from extension's serializeWitness)
|
|
118
124
|
* @param options Optional txnDifficulty override and test PRNG
|
|
119
125
|
*/
|
|
120
|
-
|
|
121
|
-
const txnDifficulty = options?.txnDifficulty ?? MAX_HASH;
|
|
126
|
+
function serializeTxBody(txBytes, witnessBytes, options) {
|
|
127
|
+
const txnDifficulty = options?.txnDifficulty ?? constants_js_1.MAX_HASH;
|
|
122
128
|
let prng;
|
|
123
129
|
if (options?.prng) {
|
|
124
130
|
prng = options.prng;
|
|
@@ -130,7 +136,7 @@ export function serializeTxBody(txBytes, witnessBytes, options) {
|
|
|
130
136
|
}
|
|
131
137
|
crypto.getRandomValues(prng);
|
|
132
138
|
}
|
|
133
|
-
return concat(writeHashToStream(prng), writeMiniData(txnDifficulty), txBytes, witnessBytes, buildEmptyBurnTxBytes(), buildEmptyBurnWitnessBytes(), writeMiniNumber(0n));
|
|
139
|
+
return (0, core_1.concat)((0, core_1.writeHashToStream)(prng), (0, core_1.writeMiniData)(txnDifficulty), txBytes, witnessBytes, buildEmptyBurnTxBytes(), buildEmptyBurnWitnessBytes(), (0, core_1.writeMiniNumber)(0n));
|
|
134
140
|
}
|
|
135
141
|
/**
|
|
136
142
|
* Serialize a complete TxPoW per Minima's TxPoW.writeDataStream().
|
|
@@ -146,8 +152,8 @@ export function serializeTxBody(txBytes, witnessBytes, options) {
|
|
|
146
152
|
* @param witnessBytes Pre-serialized Witness bytes
|
|
147
153
|
* @param options Optional txnDifficulty, nonce, timeMilli, prng
|
|
148
154
|
*/
|
|
149
|
-
|
|
155
|
+
function serializeTxPoW(txBytes, witnessBytes, options) {
|
|
150
156
|
const txBody = serializeTxBody(txBytes, witnessBytes, options);
|
|
151
|
-
const txBodyHash = sha3_256(txBody);
|
|
152
|
-
return concat(serializeTxHeader(txBodyHash, options), new Uint8Array([0x01]), txBody);
|
|
157
|
+
const txBodyHash = (0, sha3_js_1.sha3_256)(txBody);
|
|
158
|
+
return (0, core_1.concat)(serializeTxHeader(txBodyHash, options), new Uint8Array([0x01]), txBody);
|
|
153
159
|
}
|
package/dist/verify.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* verify.ts — TxPoW proof-of-work verification.
|
|
3
4
|
*
|
|
@@ -9,9 +10,12 @@
|
|
|
9
10
|
* the network minimum difficulty. PureMinima performs authoritative validation
|
|
10
11
|
* on submission; this is a first-pass spam filter.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.verifyProofOfWork = verifyProofOfWork;
|
|
15
|
+
exports.verifyTxPoWWork = verifyTxPoWWork;
|
|
16
|
+
const mine_js_1 = require("./mine.js");
|
|
17
|
+
const serialization_js_1 = require("./serialization.js");
|
|
18
|
+
const constants_js_1 = require("./constants.js");
|
|
15
19
|
function toHex(bytes) {
|
|
16
20
|
return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
17
21
|
}
|
|
@@ -23,7 +27,7 @@ function toHex(bytes) {
|
|
|
23
27
|
* @param txpowId The 32-byte TxPoW ID (SHA3-256 of the header).
|
|
24
28
|
* @param mTxnDifficulty The 32-byte difficulty target from the TxBody.
|
|
25
29
|
*/
|
|
26
|
-
|
|
30
|
+
function verifyProofOfWork(txpowId, mTxnDifficulty) {
|
|
27
31
|
if (txpowId.length !== 32) {
|
|
28
32
|
return {
|
|
29
33
|
valid: false,
|
|
@@ -40,7 +44,7 @@ export function verifyProofOfWork(txpowId, mTxnDifficulty) {
|
|
|
40
44
|
reason: `mTxnDifficulty must be 32 bytes, got ${mTxnDifficulty.length}`,
|
|
41
45
|
};
|
|
42
46
|
}
|
|
43
|
-
const valid = isLessThan(txpowId, mTxnDifficulty);
|
|
47
|
+
const valid = (0, mine_js_1.isLessThan)(txpowId, mTxnDifficulty);
|
|
44
48
|
return {
|
|
45
49
|
valid,
|
|
46
50
|
txpowId: toHex(txpowId),
|
|
@@ -58,12 +62,12 @@ export function verifyProofOfWork(txpowId, mTxnDifficulty) {
|
|
|
58
62
|
* Note: full structural parsing of TxPoW headers is deferred to a future
|
|
59
63
|
* parser. PureMinima performs authoritative work verification on submission.
|
|
60
64
|
*/
|
|
61
|
-
|
|
65
|
+
function verifyTxPoWWork(txpowHex) {
|
|
62
66
|
if (typeof txpowHex !== 'string' || !/^[0-9a-fA-F]+$/.test(txpowHex)) {
|
|
63
67
|
return {
|
|
64
68
|
valid: false,
|
|
65
69
|
txpowId: '',
|
|
66
|
-
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
70
|
+
difficulty: toHex(constants_js_1.TX_POW_MIN_DIFFICULTY),
|
|
67
71
|
reason: 'invalid hex encoding',
|
|
68
72
|
};
|
|
69
73
|
}
|
|
@@ -71,11 +75,11 @@ export function verifyTxPoWWork(txpowHex) {
|
|
|
71
75
|
return {
|
|
72
76
|
valid: false,
|
|
73
77
|
txpowId: '',
|
|
74
|
-
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
78
|
+
difficulty: toHex(constants_js_1.TX_POW_MIN_DIFFICULTY),
|
|
75
79
|
reason: 'TxPoW too short (spam filter: minimum 100 bytes)',
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
const bytes = Uint8Array.from(Buffer.from(txpowHex, 'hex'));
|
|
79
|
-
const proxyId = computeTxPoWId(bytes);
|
|
80
|
-
return verifyProofOfWork(proxyId, TX_POW_MIN_DIFFICULTY);
|
|
83
|
+
const proxyId = (0, serialization_js_1.computeTxPoWId)(bytes);
|
|
84
|
+
return verifyProofOfWork(proxyId, constants_js_1.TX_POW_MIN_DIFFICULTY);
|
|
81
85
|
}
|
package/package.json
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/txpow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "TxPoW envelope serialization and proof-of-work mining for the Minima protocol",
|
|
5
|
-
"type": "module",
|
|
6
5
|
"main": "dist/index.js",
|
|
7
6
|
"types": "dist/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
-
"
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
17
18
|
],
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@totemsdk/core": "^1.0.9"
|
|
20
21
|
},
|
|
21
22
|
"peerDependencies": {
|
|
22
|
-
"@noble/hashes": "
|
|
23
|
+
"@noble/hashes": "^2.0.0"
|
|
23
24
|
},
|
|
24
25
|
"peerDependenciesMeta": {
|
|
25
26
|
"@noble/hashes": {
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
}
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
|
-
"@noble/hashes": "^
|
|
31
|
+
"@noble/hashes": "^2.2.0",
|
|
31
32
|
"@types/jest": "^29.0.0",
|
|
32
33
|
"@types/node": "^20.0.0",
|
|
33
34
|
"jest": "^29.0.0",
|
|
@@ -41,6 +42,26 @@
|
|
|
41
42
|
"access": "public"
|
|
42
43
|
},
|
|
43
44
|
"license": "MIT",
|
|
45
|
+
"author": "Totem SDK",
|
|
46
|
+
"homepage": "https://totemsdk.com",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/MrGheek/axia-totem/issues"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/MrGheek/axia-totem.git",
|
|
53
|
+
"directory": "packages/totem-sdk/packages/txpow"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"totem",
|
|
57
|
+
"totemsdk",
|
|
58
|
+
"minima",
|
|
59
|
+
"blockchain",
|
|
60
|
+
"quantum-resistant",
|
|
61
|
+
"wots",
|
|
62
|
+
"kissvm",
|
|
63
|
+
"utxo"
|
|
64
|
+
],
|
|
44
65
|
"scripts": {
|
|
45
66
|
"build": "tsc",
|
|
46
67
|
"clean": "rm -rf dist",
|