@totemsdk/txpow 1.0.2 → 1.1.1
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 +202 -21
- package/NOTICE +60 -0
- 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 +34 -15
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify.ts — TxPoW proof-of-work verification.
|
|
3
|
+
*
|
|
4
|
+
* verifyProofOfWork checks that a given txpowId beats the difficulty target
|
|
5
|
+
* without re-mining. Used by relay nodes to reject under-work submissions.
|
|
6
|
+
*
|
|
7
|
+
* verifyTxPoWWork receives raw TxPoW hex, parses the header/body split by
|
|
8
|
+
* verifying the embedded body-hash field, computes the canonical txpowId
|
|
9
|
+
* (SHA3-256 of the header only), extracts mTxnDifficulty from the TxBody,
|
|
10
|
+
* and checks txpowId < mTxnDifficulty. Falls back to the TX_POW_MIN_DIFFICULTY
|
|
11
|
+
* floor when parsing fails (malformed or non-standard TxPoW).
|
|
12
|
+
*
|
|
13
|
+
* verifyTxPoWParts is the low-level variant for callers who already hold
|
|
14
|
+
* pre-split header/body bytes.
|
|
15
|
+
*
|
|
16
|
+
* ── Wire format reference ─────────────────────────────────────────────────
|
|
17
|
+
* @totemsdk/core's writeHashToStream(hash) uses a 4-byte big-endian length
|
|
18
|
+
* prefix followed by the bytes: for a 32-byte hash → [0x00,0x00,0x00,0x20, ...32 bytes]
|
|
19
|
+
* = 36 bytes total. writeMiniData uses the same 4-byte prefix format.
|
|
20
|
+
*
|
|
21
|
+
* TxPoW wire format: TxHeader bytes | 0x01 (hasBody) | TxBody bytes
|
|
22
|
+
* TxPoW ID: SHA3-256(TxHeader bytes)
|
|
23
|
+
*
|
|
24
|
+
* TxHeader ends with:
|
|
25
|
+
* writeHashToStream(mCustomHash) — [0x00,0x00,0x00,0x20, ...32 zeros] 36 bytes
|
|
26
|
+
* writeHashToStream(mTxBodyHash) — [0x00,0x00,0x00,0x20, ...32 bytes] 36 bytes
|
|
27
|
+
*
|
|
28
|
+
* TxBody starts with:
|
|
29
|
+
* writeHashToStream(mPRNG) — [0x00,0x00,0x00,0x20, ...32 bytes] 36 bytes
|
|
30
|
+
* writeMiniData(mTxnDifficulty) — [0x00,0x00,0x00,len, ...len bytes] 4+len bytes
|
|
31
|
+
*/
|
|
32
|
+
export interface VerifyResult {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
txpowId: string;
|
|
35
|
+
difficulty: string;
|
|
36
|
+
reason?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Verify that a TxPoW ID beats the stated difficulty target.
|
|
40
|
+
*
|
|
41
|
+
* valid = txpowId < mTxnDifficulty (big-endian 256-bit comparison)
|
|
42
|
+
*
|
|
43
|
+
* @param txpowId The 32-byte TxPoW ID (SHA3-256 of the header).
|
|
44
|
+
* @param mTxnDifficulty The 32-byte difficulty target from the TxBody.
|
|
45
|
+
*/
|
|
46
|
+
export declare function verifyProofOfWork(txpowId: Uint8Array, mTxnDifficulty: Uint8Array): VerifyResult;
|
|
47
|
+
/**
|
|
48
|
+
* Verify a TxPoW from pre-split header and body bytes.
|
|
49
|
+
*
|
|
50
|
+
* Computes SHA3-256(headerBytes) as the txpowId, extracts mTxnDifficulty
|
|
51
|
+
* from the body, and checks txpowId < mTxnDifficulty. Falls back to
|
|
52
|
+
* TX_POW_MIN_DIFFICULTY when the body cannot be parsed.
|
|
53
|
+
*
|
|
54
|
+
* @param headerBytes Raw TxHeader bytes (SHA3-256 of these is the txpowId).
|
|
55
|
+
* @param bodyBytes Raw TxBody bytes (mTxnDifficulty extracted from here).
|
|
56
|
+
*/
|
|
57
|
+
export declare function verifyTxPoWParts(headerBytes: Uint8Array, bodyBytes: Uint8Array): VerifyResult;
|
|
58
|
+
/**
|
|
59
|
+
* Relay-side work verification from raw TxPoW hex.
|
|
60
|
+
*
|
|
61
|
+
* Parses the TxPoW hex into header and body by locating the hasBody byte via
|
|
62
|
+
* body-hash verification. Computes the canonical TxPoW ID (SHA3-256 of the
|
|
63
|
+
* header only), extracts mTxnDifficulty from the TxBody, and verifies:
|
|
64
|
+
* txpowId < mTxnDifficulty
|
|
65
|
+
*
|
|
66
|
+
* Falls back to TX_POW_MIN_DIFFICULTY as a spam filter when:
|
|
67
|
+
* - The hex is invalid or malformed
|
|
68
|
+
* - No valid header/body split can be found (non-standard structure)
|
|
69
|
+
* - mTxnDifficulty cannot be extracted from the body
|
|
70
|
+
*
|
|
71
|
+
* PureMinima performs authoritative work verification on submission; this
|
|
72
|
+
* function is a first-pass relay-side filter.
|
|
73
|
+
*
|
|
74
|
+
* @param txpowHex Hex-encoded serialized TxPoW (TxHeader | 0x01 | TxBody).
|
|
75
|
+
*/
|
|
76
|
+
export declare function verifyTxPoWWork(txpowHex: string): VerifyResult;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* verify.ts — TxPoW proof-of-work verification.
|
|
4
|
+
*
|
|
5
|
+
* verifyProofOfWork checks that a given txpowId beats the difficulty target
|
|
6
|
+
* without re-mining. Used by relay nodes to reject under-work submissions.
|
|
7
|
+
*
|
|
8
|
+
* verifyTxPoWWork receives raw TxPoW hex, parses the header/body split by
|
|
9
|
+
* verifying the embedded body-hash field, computes the canonical txpowId
|
|
10
|
+
* (SHA3-256 of the header only), extracts mTxnDifficulty from the TxBody,
|
|
11
|
+
* and checks txpowId < mTxnDifficulty. Falls back to the TX_POW_MIN_DIFFICULTY
|
|
12
|
+
* floor when parsing fails (malformed or non-standard TxPoW).
|
|
13
|
+
*
|
|
14
|
+
* verifyTxPoWParts is the low-level variant for callers who already hold
|
|
15
|
+
* pre-split header/body bytes.
|
|
16
|
+
*
|
|
17
|
+
* ── Wire format reference ─────────────────────────────────────────────────
|
|
18
|
+
* @totemsdk/core's writeHashToStream(hash) uses a 4-byte big-endian length
|
|
19
|
+
* prefix followed by the bytes: for a 32-byte hash → [0x00,0x00,0x00,0x20, ...32 bytes]
|
|
20
|
+
* = 36 bytes total. writeMiniData uses the same 4-byte prefix format.
|
|
21
|
+
*
|
|
22
|
+
* TxPoW wire format: TxHeader bytes | 0x01 (hasBody) | TxBody bytes
|
|
23
|
+
* TxPoW ID: SHA3-256(TxHeader bytes)
|
|
24
|
+
*
|
|
25
|
+
* TxHeader ends with:
|
|
26
|
+
* writeHashToStream(mCustomHash) — [0x00,0x00,0x00,0x20, ...32 zeros] 36 bytes
|
|
27
|
+
* writeHashToStream(mTxBodyHash) — [0x00,0x00,0x00,0x20, ...32 bytes] 36 bytes
|
|
28
|
+
*
|
|
29
|
+
* TxBody starts with:
|
|
30
|
+
* writeHashToStream(mPRNG) — [0x00,0x00,0x00,0x20, ...32 bytes] 36 bytes
|
|
31
|
+
* writeMiniData(mTxnDifficulty) — [0x00,0x00,0x00,len, ...len bytes] 4+len bytes
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.verifyProofOfWork = verifyProofOfWork;
|
|
35
|
+
exports.verifyTxPoWParts = verifyTxPoWParts;
|
|
36
|
+
exports.verifyTxPoWWork = verifyTxPoWWork;
|
|
37
|
+
const core_1 = require("@totemsdk/core");
|
|
38
|
+
const mine_js_1 = require("./mine.js");
|
|
39
|
+
const serialization_js_1 = require("./serialization.js");
|
|
40
|
+
const constants_js_1 = require("./constants.js");
|
|
41
|
+
function toHex(bytes) {
|
|
42
|
+
return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
43
|
+
}
|
|
44
|
+
/** Read a big-endian 4-byte unsigned integer from bytes at offset. */
|
|
45
|
+
function readUint32BE(bytes, offset) {
|
|
46
|
+
return (((bytes[offset] << 24) |
|
|
47
|
+
(bytes[offset + 1] << 16) |
|
|
48
|
+
(bytes[offset + 2] << 8) |
|
|
49
|
+
bytes[offset + 3]) >>>
|
|
50
|
+
0);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Verify that a TxPoW ID beats the stated difficulty target.
|
|
54
|
+
*
|
|
55
|
+
* valid = txpowId < mTxnDifficulty (big-endian 256-bit comparison)
|
|
56
|
+
*
|
|
57
|
+
* @param txpowId The 32-byte TxPoW ID (SHA3-256 of the header).
|
|
58
|
+
* @param mTxnDifficulty The 32-byte difficulty target from the TxBody.
|
|
59
|
+
*/
|
|
60
|
+
function verifyProofOfWork(txpowId, mTxnDifficulty) {
|
|
61
|
+
if (txpowId.length !== 32) {
|
|
62
|
+
return {
|
|
63
|
+
valid: false,
|
|
64
|
+
txpowId: toHex(txpowId),
|
|
65
|
+
difficulty: toHex(mTxnDifficulty),
|
|
66
|
+
reason: `txpowId must be 32 bytes, got ${txpowId.length}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (mTxnDifficulty.length !== 32) {
|
|
70
|
+
return {
|
|
71
|
+
valid: false,
|
|
72
|
+
txpowId: toHex(txpowId),
|
|
73
|
+
difficulty: toHex(mTxnDifficulty),
|
|
74
|
+
reason: `mTxnDifficulty must be 32 bytes, got ${mTxnDifficulty.length}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const valid = (0, mine_js_1.isLessThan)(txpowId, mTxnDifficulty);
|
|
78
|
+
return {
|
|
79
|
+
valid,
|
|
80
|
+
txpowId: toHex(txpowId),
|
|
81
|
+
difficulty: toHex(mTxnDifficulty),
|
|
82
|
+
reason: valid ? undefined : 'txpowId ≥ mTxnDifficulty',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
86
|
+
// TxPoW header/body parser
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Attempt to split raw TxPoW bytes into (headerBytes, bodyBytes).
|
|
90
|
+
*
|
|
91
|
+
* TxPoW wire format: TxHeader | 0x01 (hasBody byte) | TxBody
|
|
92
|
+
* TxPoW ID: SHA3-256(TxHeader)
|
|
93
|
+
*
|
|
94
|
+
* The header always ends with `writeHashToStream(sha3_256(TxBody))`.
|
|
95
|
+
* @totemsdk/core writeHashToStream uses a 4-byte big-endian length prefix:
|
|
96
|
+
* [0x00, 0x00, 0x00, 0x20, ...32 body-hash bytes] = 36 bytes
|
|
97
|
+
*
|
|
98
|
+
* We scan forward looking for a 0x01 hasBody byte where:
|
|
99
|
+
* - The preceding 36 bytes are [0,0,0,32] + 32-byte bodyHash
|
|
100
|
+
* - sha3_256(bodyBytes) == those 32 bytes
|
|
101
|
+
* When found the split is authenticated by the hash.
|
|
102
|
+
*
|
|
103
|
+
* Returns null when no valid split is found.
|
|
104
|
+
*/
|
|
105
|
+
function tryParseTxPoW(bytes) {
|
|
106
|
+
// writeHashToStream(32-byte hash) = 36 bytes
|
|
107
|
+
const HASH_FIELD_LEN = 36;
|
|
108
|
+
// Minimum body: PRNG (36 bytes) + mTxnDifficulty prefix+data (4+1 minimum)
|
|
109
|
+
const MIN_BODY = 41;
|
|
110
|
+
// Minimum header must end with txBodyHash field (36 bytes)
|
|
111
|
+
const MIN_HEADER = HASH_FIELD_LEN;
|
|
112
|
+
const MIN_TOTAL = MIN_HEADER + 1 + MIN_BODY;
|
|
113
|
+
if (bytes.length < MIN_TOTAL)
|
|
114
|
+
return null;
|
|
115
|
+
for (let i = MIN_HEADER; i <= bytes.length - MIN_BODY - 1; i++) {
|
|
116
|
+
if (bytes[i] !== 0x01)
|
|
117
|
+
continue;
|
|
118
|
+
const headerBytes = bytes.slice(0, i);
|
|
119
|
+
if (headerBytes.length < HASH_FIELD_LEN)
|
|
120
|
+
continue;
|
|
121
|
+
// Last 36 bytes of header = writeHashToStream(bodyHash)
|
|
122
|
+
// = [0x00, 0x00, 0x00, 0x20, ...32 bytes]
|
|
123
|
+
const hashFieldStart = headerBytes.length - HASH_FIELD_LEN;
|
|
124
|
+
if (headerBytes[hashFieldStart] !== 0x00 ||
|
|
125
|
+
headerBytes[hashFieldStart + 1] !== 0x00 ||
|
|
126
|
+
headerBytes[hashFieldStart + 2] !== 0x00 ||
|
|
127
|
+
headerBytes[hashFieldStart + 3] !== 0x20) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const embeddedBodyHash = headerBytes.slice(hashFieldStart + 4);
|
|
131
|
+
const bodyBytes = bytes.slice(i + 1);
|
|
132
|
+
const actualBodyHash = (0, core_1.sha3_256)(bodyBytes);
|
|
133
|
+
if (actualBodyHash.every((b, j) => b === embeddedBodyHash[j])) {
|
|
134
|
+
return { headerBytes, bodyBytes };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Extract mTxnDifficulty from TxBody bytes.
|
|
141
|
+
*
|
|
142
|
+
* TxBody wire format (using @totemsdk/core serialization):
|
|
143
|
+
* [0..3] [0x00,0x00,0x00,0x20] 4-byte big-endian length (32) for PRNG
|
|
144
|
+
* [4..35] prng bytes 32-byte random nonce
|
|
145
|
+
* [36..39] [0x00,0x00,0x00,len] 4-byte big-endian length for mTxnDifficulty
|
|
146
|
+
* [40..40+len-1] difficulty bytes len bytes (standard: 32)
|
|
147
|
+
*
|
|
148
|
+
* Returns null when the body is too short or length prefixes are invalid.
|
|
149
|
+
*/
|
|
150
|
+
function extractTxnDifficulty(bodyBytes) {
|
|
151
|
+
// PRNG field: 4-byte length prefix + 32 bytes = 36 bytes total
|
|
152
|
+
if (bodyBytes.length < 40)
|
|
153
|
+
return null;
|
|
154
|
+
// Length prefix for PRNG must be 0x00000020 (= 32)
|
|
155
|
+
if (bodyBytes[0] !== 0x00 ||
|
|
156
|
+
bodyBytes[1] !== 0x00 ||
|
|
157
|
+
bodyBytes[2] !== 0x00 ||
|
|
158
|
+
bodyBytes[3] !== 0x20) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
// After PRNG (36 bytes), the next 4 bytes are the length prefix for mTxnDifficulty
|
|
162
|
+
const diffLen = readUint32BE(bodyBytes, 36);
|
|
163
|
+
if (diffLen === 0 || 40 + diffLen > bodyBytes.length)
|
|
164
|
+
return null;
|
|
165
|
+
return bodyBytes.slice(40, 40 + diffLen);
|
|
166
|
+
}
|
|
167
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
168
|
+
// Public verification API
|
|
169
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
170
|
+
/**
|
|
171
|
+
* Verify a TxPoW from pre-split header and body bytes.
|
|
172
|
+
*
|
|
173
|
+
* Computes SHA3-256(headerBytes) as the txpowId, extracts mTxnDifficulty
|
|
174
|
+
* from the body, and checks txpowId < mTxnDifficulty. Falls back to
|
|
175
|
+
* TX_POW_MIN_DIFFICULTY when the body cannot be parsed.
|
|
176
|
+
*
|
|
177
|
+
* @param headerBytes Raw TxHeader bytes (SHA3-256 of these is the txpowId).
|
|
178
|
+
* @param bodyBytes Raw TxBody bytes (mTxnDifficulty extracted from here).
|
|
179
|
+
*/
|
|
180
|
+
function verifyTxPoWParts(headerBytes, bodyBytes) {
|
|
181
|
+
const txpowId = (0, serialization_js_1.computeTxPoWId)(headerBytes);
|
|
182
|
+
const difficulty = extractTxnDifficulty(bodyBytes) ?? constants_js_1.TX_POW_MIN_DIFFICULTY;
|
|
183
|
+
return verifyProofOfWork(txpowId, difficulty);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Relay-side work verification from raw TxPoW hex.
|
|
187
|
+
*
|
|
188
|
+
* Parses the TxPoW hex into header and body by locating the hasBody byte via
|
|
189
|
+
* body-hash verification. Computes the canonical TxPoW ID (SHA3-256 of the
|
|
190
|
+
* header only), extracts mTxnDifficulty from the TxBody, and verifies:
|
|
191
|
+
* txpowId < mTxnDifficulty
|
|
192
|
+
*
|
|
193
|
+
* Falls back to TX_POW_MIN_DIFFICULTY as a spam filter when:
|
|
194
|
+
* - The hex is invalid or malformed
|
|
195
|
+
* - No valid header/body split can be found (non-standard structure)
|
|
196
|
+
* - mTxnDifficulty cannot be extracted from the body
|
|
197
|
+
*
|
|
198
|
+
* PureMinima performs authoritative work verification on submission; this
|
|
199
|
+
* function is a first-pass relay-side filter.
|
|
200
|
+
*
|
|
201
|
+
* @param txpowHex Hex-encoded serialized TxPoW (TxHeader | 0x01 | TxBody).
|
|
202
|
+
*/
|
|
203
|
+
function verifyTxPoWWork(txpowHex) {
|
|
204
|
+
if (typeof txpowHex !== 'string' || !/^[0-9a-fA-F]+$/.test(txpowHex)) {
|
|
205
|
+
return {
|
|
206
|
+
valid: false,
|
|
207
|
+
txpowId: '',
|
|
208
|
+
difficulty: toHex(constants_js_1.TX_POW_MIN_DIFFICULTY),
|
|
209
|
+
reason: 'invalid hex encoding',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (txpowHex.length < 200) {
|
|
213
|
+
return {
|
|
214
|
+
valid: false,
|
|
215
|
+
txpowId: '',
|
|
216
|
+
difficulty: toHex(constants_js_1.TX_POW_MIN_DIFFICULTY),
|
|
217
|
+
reason: 'TxPoW too short (spam filter: minimum 100 bytes)',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const bytes = Uint8Array.from(Buffer.from(txpowHex, 'hex'));
|
|
221
|
+
// Attempt to parse the TxPoW into header + body using body-hash verification
|
|
222
|
+
const parsed = tryParseTxPoW(bytes);
|
|
223
|
+
if (parsed) {
|
|
224
|
+
return verifyTxPoWParts(parsed.headerBytes, parsed.bodyBytes);
|
|
225
|
+
}
|
|
226
|
+
// Fallback: use sha3_256 of the full bytes as a proxy id against the floor
|
|
227
|
+
const proxyId = (0, serialization_js_1.computeTxPoWId)(bytes);
|
|
228
|
+
return {
|
|
229
|
+
...verifyProofOfWork(proxyId, constants_js_1.TX_POW_MIN_DIFFICULTY),
|
|
230
|
+
reason: 'could not parse TxPoW structure — used proxy id against floor difficulty',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser.worker.ts — Browser Web Worker entry point for TxPoW mining.
|
|
3
|
+
*
|
|
4
|
+
* This file is the Web Worker counterpart to node.worker.ts.
|
|
5
|
+
* It is designed to be bundled by the extension's build system (webpack / vite)
|
|
6
|
+
* as part of Task #114. It cannot be loaded directly from a CJS context.
|
|
7
|
+
*
|
|
8
|
+
* Usage (from the extension):
|
|
9
|
+
* import MinerWorker from '@totemsdk/txpow/dist/workers/browser.worker.js?worker';
|
|
10
|
+
* const worker = new MinerWorker();
|
|
11
|
+
* worker.postMessage({ txBodyBytes, txnDifficulty, wasmUrl, options });
|
|
12
|
+
* worker.onmessage = (e) => { const result: MineResult = e.data; };
|
|
13
|
+
*
|
|
14
|
+
* Communication protocol (mirrors node.worker.ts):
|
|
15
|
+
* Input (onmessage e.data): BrowserWorkerInput — serialized mining parameters
|
|
16
|
+
* Output (postMessage): BrowserWorkerOutput — serialized MineResult or error
|
|
17
|
+
*
|
|
18
|
+
* WASM in the worker:
|
|
19
|
+
* The browser Web Worker runs in its own JS context, so `setWasmUrl()` called
|
|
20
|
+
* on the main thread does NOT carry over. Pass the WASM URL in the input
|
|
21
|
+
* message (`wasmUrl` field) so the worker can call `setWasmUrl()` before
|
|
22
|
+
* starting the mining loop. Without `wasmUrl`, the worker mines in pure JS.
|
|
23
|
+
*
|
|
24
|
+
* See mine.ts for details on the BigInt string encoding and Uint8Array [] encoding.
|
|
25
|
+
*/
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* browser.worker.ts — Browser Web Worker entry point for TxPoW mining.
|
|
4
|
+
*
|
|
5
|
+
* This file is the Web Worker counterpart to node.worker.ts.
|
|
6
|
+
* It is designed to be bundled by the extension's build system (webpack / vite)
|
|
7
|
+
* as part of Task #114. It cannot be loaded directly from a CJS context.
|
|
8
|
+
*
|
|
9
|
+
* Usage (from the extension):
|
|
10
|
+
* import MinerWorker from '@totemsdk/txpow/dist/workers/browser.worker.js?worker';
|
|
11
|
+
* const worker = new MinerWorker();
|
|
12
|
+
* worker.postMessage({ txBodyBytes, txnDifficulty, wasmUrl, options });
|
|
13
|
+
* worker.onmessage = (e) => { const result: MineResult = e.data; };
|
|
14
|
+
*
|
|
15
|
+
* Communication protocol (mirrors node.worker.ts):
|
|
16
|
+
* Input (onmessage e.data): BrowserWorkerInput — serialized mining parameters
|
|
17
|
+
* Output (postMessage): BrowserWorkerOutput — serialized MineResult or error
|
|
18
|
+
*
|
|
19
|
+
* WASM in the worker:
|
|
20
|
+
* The browser Web Worker runs in its own JS context, so `setWasmUrl()` called
|
|
21
|
+
* on the main thread does NOT carry over. Pass the WASM URL in the input
|
|
22
|
+
* message (`wasmUrl` field) so the worker can call `setWasmUrl()` before
|
|
23
|
+
* starting the mining loop. Without `wasmUrl`, the worker mines in pure JS.
|
|
24
|
+
*
|
|
25
|
+
* See mine.ts for details on the BigInt string encoding and Uint8Array [] encoding.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
/// <reference lib="webworker" />
|
|
29
|
+
const mine_js_1 = require("../mine.js");
|
|
30
|
+
const mine_wasm_js_1 = require("../mine-wasm.js");
|
|
31
|
+
self.onmessage = async (e) => {
|
|
32
|
+
const input = e.data;
|
|
33
|
+
// Configure WASM inside this worker's JS context before mining starts.
|
|
34
|
+
// The main thread's setWasmUrl() call does not carry over to a Web Worker.
|
|
35
|
+
if (input.wasmUrl) {
|
|
36
|
+
(0, mine_wasm_js_1.setWasmUrl)(input.wasmUrl);
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const result = await (0, mine_js_1.mineTxPoWInProcess)(new Uint8Array(input.txBodyBytes), new Uint8Array(input.txnDifficulty), {
|
|
40
|
+
chunkSize: input.chunkSize,
|
|
41
|
+
maxIterations: input.maxIterations !== undefined
|
|
42
|
+
? Number(input.maxIterations)
|
|
43
|
+
: undefined,
|
|
44
|
+
timeMilli: input.timeMilli !== undefined ? BigInt(input.timeMilli) : undefined,
|
|
45
|
+
forceJs: input.forceJs,
|
|
46
|
+
_skipWorker: true,
|
|
47
|
+
});
|
|
48
|
+
const output = {
|
|
49
|
+
minedHeaderBytes: Array.from(result.minedHeaderBytes),
|
|
50
|
+
txpowId: Array.from(result.txpowId),
|
|
51
|
+
nonce: result.nonce.toString(),
|
|
52
|
+
elapsedMs: result.elapsedMs,
|
|
53
|
+
source: result.source,
|
|
54
|
+
};
|
|
55
|
+
self.postMessage(output);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const output = {
|
|
59
|
+
error: err instanceof Error ? err.message : String(err),
|
|
60
|
+
};
|
|
61
|
+
self.postMessage(output);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* node.worker.ts — Node.js worker_threads entry point for TxPoW mining.
|
|
3
|
+
*
|
|
4
|
+
* This file is compiled to `dist/workers/node.worker.js` and spawned by
|
|
5
|
+
* `mineTxPoW()` in the main thread via `new Worker(workerPath, { workerData })`.
|
|
6
|
+
*
|
|
7
|
+
* Communication protocol:
|
|
8
|
+
* Input (workerData): NodeWorkerInput — serialized mining parameters
|
|
9
|
+
* Output (postMessage): NodeWorkerOutput — serialized MineResult or error
|
|
10
|
+
*
|
|
11
|
+
* BigInt values (nonce, timeMilli, maxIterations) are transmitted as strings
|
|
12
|
+
* to avoid structured-clone limitations on older Node.js versions.
|
|
13
|
+
* Uint8Arrays are transmitted as number[] for reliable structured-clone transfer.
|
|
14
|
+
*/
|
|
15
|
+
declare const workerData: any, parentPort: import("worker_threads").MessagePort | null;
|
|
16
|
+
declare const mineTxPoWInProcess: typeof import("../mine.js").mineTxPoWInProcess;
|
|
17
|
+
interface NodeWorkerInput {
|
|
18
|
+
txBodyBytes: number[];
|
|
19
|
+
txnDifficulty: number[];
|
|
20
|
+
chunkSize?: number;
|
|
21
|
+
maxIterations?: string;
|
|
22
|
+
timeMilli?: string;
|
|
23
|
+
forceJs?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface NodeWorkerOutput {
|
|
26
|
+
minedHeaderBytes?: number[];
|
|
27
|
+
txpowId?: number[];
|
|
28
|
+
nonce?: string;
|
|
29
|
+
elapsedMs?: number;
|
|
30
|
+
source?: 'wasm' | 'js';
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
declare function run(): Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* node.worker.ts — Node.js worker_threads entry point for TxPoW mining.
|
|
4
|
+
*
|
|
5
|
+
* This file is compiled to `dist/workers/node.worker.js` and spawned by
|
|
6
|
+
* `mineTxPoW()` in the main thread via `new Worker(workerPath, { workerData })`.
|
|
7
|
+
*
|
|
8
|
+
* Communication protocol:
|
|
9
|
+
* Input (workerData): NodeWorkerInput — serialized mining parameters
|
|
10
|
+
* Output (postMessage): NodeWorkerOutput — serialized MineResult or error
|
|
11
|
+
*
|
|
12
|
+
* BigInt values (nonce, timeMilli, maxIterations) are transmitted as strings
|
|
13
|
+
* to avoid structured-clone limitations on older Node.js versions.
|
|
14
|
+
* Uint8Arrays are transmitted as number[] for reliable structured-clone transfer.
|
|
15
|
+
*/
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
17
|
+
const { workerData, parentPort } = require('worker_threads');
|
|
18
|
+
const { mineTxPoWInProcess } = require('../mine.js');
|
|
19
|
+
async function run() {
|
|
20
|
+
const input = workerData;
|
|
21
|
+
try {
|
|
22
|
+
const result = await mineTxPoWInProcess(new Uint8Array(input.txBodyBytes), new Uint8Array(input.txnDifficulty), {
|
|
23
|
+
chunkSize: input.chunkSize,
|
|
24
|
+
maxIterations: input.maxIterations !== undefined
|
|
25
|
+
? Number(input.maxIterations)
|
|
26
|
+
: undefined,
|
|
27
|
+
timeMilli: input.timeMilli !== undefined ? BigInt(input.timeMilli) : undefined,
|
|
28
|
+
forceJs: input.forceJs,
|
|
29
|
+
_skipWorker: true,
|
|
30
|
+
});
|
|
31
|
+
const output = {
|
|
32
|
+
minedHeaderBytes: Array.from(result.minedHeaderBytes),
|
|
33
|
+
txpowId: Array.from(result.txpowId),
|
|
34
|
+
nonce: result.nonce.toString(),
|
|
35
|
+
elapsedMs: result.elapsedMs,
|
|
36
|
+
source: result.source,
|
|
37
|
+
};
|
|
38
|
+
parentPort?.postMessage(output);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const output = {
|
|
42
|
+
error: err instanceof Error ? err.message : String(err),
|
|
43
|
+
};
|
|
44
|
+
parentPort?.postMessage(output);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
run();
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* constants.ts — TxPoW-level constants matching Minima Java protocol values.
|
|
4
3
|
*
|
|
@@ -11,11 +10,9 @@
|
|
|
11
10
|
* MAIN_NET_CHAIN_ID: 1-byte [0x00] — Java MiniData("0x00") = MAIN_NET chain ID.
|
|
12
11
|
* CASCADE_LEVELS: 32 — number of super-parent slots in a fresh TxPoW header.
|
|
13
12
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
exports.ZERO_HASH = new Uint8Array(32);
|
|
18
|
-
exports.MAX_HASH = (() => {
|
|
13
|
+
export const CASCADE_LEVELS = 32;
|
|
14
|
+
export const ZERO_HASH = new Uint8Array(32);
|
|
15
|
+
export const MAX_HASH = (() => {
|
|
19
16
|
const b = new Uint8Array(32);
|
|
20
17
|
b.fill(0xff);
|
|
21
18
|
return b;
|
|
@@ -30,7 +27,7 @@ exports.MAX_HASH = (() => {
|
|
|
30
27
|
* Computed: (2n**256n - 1n) / 1_000_000n
|
|
31
28
|
* Hex: 0x000010C6F7A0B5ED8538AACDD46595F0C7AC73E0E9DBF12F70000000000000000
|
|
32
29
|
*/
|
|
33
|
-
|
|
30
|
+
export const TX_POW_MIN_DIFFICULTY = (() => {
|
|
34
31
|
const maxHashBigInt = (2n ** 256n) - 1n;
|
|
35
32
|
const minDiffBigInt = maxHashBigInt / 1000000n;
|
|
36
33
|
let hex = minDiffBigInt.toString(16);
|
|
@@ -44,4 +41,4 @@ exports.TX_POW_MIN_DIFFICULTY = (() => {
|
|
|
44
41
|
}
|
|
45
42
|
return bytes;
|
|
46
43
|
})();
|
|
47
|
-
|
|
44
|
+
export const MAIN_NET_CHAIN_ID = new Uint8Array([0x00]);
|