@totemsdk/txpow 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/admission/challenge.js +14 -21
  2. package/dist/admission/commitment.js +9 -13
  3. package/dist/admission/index.js +6 -34
  4. package/dist/admission/mine.js +14 -17
  5. package/dist/admission/template.js +32 -47
  6. package/dist/admission/types.js +2 -5
  7. package/dist/admission/verify.js +12 -15
  8. package/dist/calibrate.js +4 -8
  9. package/dist/cjs/admission/challenge.d.ts +66 -0
  10. package/dist/cjs/admission/challenge.js +155 -0
  11. package/dist/cjs/admission/commitment.d.ts +34 -0
  12. package/dist/cjs/admission/commitment.js +73 -0
  13. package/dist/cjs/admission/index.d.ts +14 -0
  14. package/dist/cjs/admission/index.js +42 -0
  15. package/dist/cjs/admission/mine.d.ts +60 -0
  16. package/dist/cjs/admission/mine.js +146 -0
  17. package/dist/cjs/admission/template.d.ts +138 -0
  18. package/dist/cjs/admission/template.js +247 -0
  19. package/dist/cjs/admission/types.d.ts +224 -0
  20. package/dist/cjs/admission/types.js +24 -0
  21. package/dist/cjs/admission/verify.d.ts +50 -0
  22. package/dist/cjs/admission/verify.js +156 -0
  23. package/dist/cjs/calibrate.d.ts +35 -0
  24. package/dist/cjs/calibrate.js +68 -0
  25. package/dist/cjs/constants.d.ts +27 -0
  26. package/dist/cjs/constants.js +47 -0
  27. package/dist/cjs/difficulty.d.ts +44 -0
  28. package/dist/cjs/difficulty.js +111 -0
  29. package/dist/cjs/index.d.ts +28 -0
  30. package/dist/cjs/index.js +88 -0
  31. package/dist/cjs/magic.d.ts +19 -0
  32. package/dist/cjs/magic.js +35 -0
  33. package/dist/cjs/mine-wasm.d.ts +69 -0
  34. package/dist/cjs/mine-wasm.js +214 -0
  35. package/dist/cjs/mine.d.ts +131 -0
  36. package/dist/cjs/mine.js +407 -0
  37. package/dist/cjs/package.json +1 -0
  38. package/dist/cjs/serialization.d.ts +106 -0
  39. package/dist/cjs/serialization.js +160 -0
  40. package/dist/cjs/verify.d.ts +76 -0
  41. package/dist/cjs/verify.js +232 -0
  42. package/dist/cjs/wasm/miner.wasm +0 -0
  43. package/dist/cjs/workers/browser.worker.d.ts +26 -0
  44. package/dist/cjs/workers/browser.worker.js +63 -0
  45. package/dist/cjs/workers/node.worker.d.ts +33 -0
  46. package/dist/cjs/workers/node.worker.js +47 -0
  47. package/dist/constants.js +5 -8
  48. package/dist/difficulty.d.ts +24 -0
  49. package/dist/difficulty.js +54 -6
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.js +9 -62
  52. package/dist/magic.js +12 -15
  53. package/dist/mine-wasm.js +8 -15
  54. package/dist/mine.js +23 -31
  55. package/dist/serialization.js +28 -34
  56. package/dist/verify.js +15 -20
  57. package/dist/workers/browser.worker.js +4 -6
  58. package/dist/workers/node.worker.js +2 -3
  59. package/package.json +31 -13
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * admission/verify.ts — Machine Work Admission verification.
4
+ *
5
+ * Verification NEVER trusts sender-reported hardware speed or self-reported
6
+ * block metadata (`isBlock`, `superLevel`, `qualifiesAsMinimaBlock`,
7
+ * `broadcastable`). It recomputes the canonical action commitment, re-derives
8
+ * the TxPoW ID from the mined header, and checks `txpowId < challenge.target`.
9
+ * It also checks challenge validity (expiry, recipient, domain) and template
10
+ * freshness.
11
+ *
12
+ * Three distinct claims, never to be confused:
13
+ * A. valid — the hash satisfies the challenge target.
14
+ * B. superLevel/isBlock — the exact Minima Super level (-1 or 0..31)
15
+ * computed from blockDifficulty / txpowId.
16
+ * C. broadcastable — isBlock AND the template is current AND a live
17
+ * template provider was supplied.
18
+ *
19
+ * A stale candidate may remain valid = true (and superLevel >= 0) while
20
+ * broadcastable = false. Offline verification (no provider) must NOT claim
21
+ * Minima block contribution — `broadcastable` is left undefined.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.verifyWorkAdmission = verifyWorkAdmission;
25
+ const core_1 = require("@totemsdk/core");
26
+ const commitment_js_1 = require("./commitment.js");
27
+ const challenge_js_1 = require("./challenge.js");
28
+ const template_js_1 = require("./template.js");
29
+ const types_js_1 = require("./types.js");
30
+ function hexToBytes(hex) {
31
+ const h = hex.startsWith('0x') ? hex.slice(2) : hex;
32
+ const out = new Uint8Array(h.length / 2);
33
+ for (let i = 0; i < h.length; i += 2) {
34
+ out[i / 2] = parseInt(h.slice(i, i + 2), 16);
35
+ }
36
+ return out;
37
+ }
38
+ /** Big-endian 256-bit comparison: true if a < b. */
39
+ function isLessThan(a, b) {
40
+ for (let i = 0; i < 32; i++) {
41
+ if (a[i] < b[i])
42
+ return true;
43
+ if (a[i] > b[i])
44
+ return false;
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * Verify a Machine Work Admission proof.
50
+ *
51
+ * The admission target is taken from the validated challenge — never from the
52
+ * proof. `proof.qualifiesAsMinimaBlock`, `proof.superLevel`, and
53
+ * `proof.isBlock` are treated as derived metadata and are NOT trusted; the
54
+ * Super level is recomputed from the re-derived txpowId and the template's
55
+ * block difficulty.
56
+ *
57
+ * @param action The application action the proof claims to commit.
58
+ * @param challenge The challenge the proof claims to satisfy.
59
+ * @param proof The mined proof.
60
+ * @param templateProvider Optional live provider. When supplied, template
61
+ * freshness and broadcastability are checked and
62
+ * `broadcastable` is set. When omitted, verification
63
+ * runs in offline mode and does NOT claim Minima
64
+ * block contribution (`broadcastable` is undefined).
65
+ * @param options Verification options.
66
+ */
67
+ async function verifyWorkAdmission(action, challenge, proof, templateProvider, options) {
68
+ // 1. Structural checks
69
+ if (!proof || typeof proof !== 'object') {
70
+ return { valid: false, reason: 'proof is not an object' };
71
+ }
72
+ if (proof.version !== types_js_1.MACHINE_WORK_ADMISSION_VERSION) {
73
+ return { valid: false, reason: `unsupported proof version ${proof.version}` };
74
+ }
75
+ if (proof.challengeId !== challenge.challengeId) {
76
+ return { valid: false, reason: 'proof challengeId does not match challenge' };
77
+ }
78
+ if (proof.qualifiesForAdmission !== true) {
79
+ return { valid: false, reason: 'proof does not claim admission' };
80
+ }
81
+ // Target manipulation guard: the proof must have been mined against the
82
+ // challenge's exact target. A sender must not be able to mine against a
83
+ // trivially easy target and present it as satisfying a harder challenge.
84
+ if (proof.admissionTarget !== challenge.target) {
85
+ return { valid: false, reason: 'proof admissionTarget does not match challenge target' };
86
+ }
87
+ // 2. Challenge validity (expiry, recipient, domain)
88
+ const challengeCheck = (0, challenge_js_1.validateWorkChallenge)(challenge, {
89
+ recipient: action.recipient,
90
+ domain: action.domain,
91
+ now: options?.now,
92
+ });
93
+ if (!challengeCheck.valid) {
94
+ return { valid: false, reason: challengeCheck.reason };
95
+ }
96
+ // 3. Recompute the canonical commitment — must match the proof
97
+ const expectedCommitment = (0, commitment_js_1.computeActionCommitment)(action, challenge);
98
+ if (expectedCommitment !== proof.actionCommitment) {
99
+ return { valid: false, reason: 'action commitment mismatch' };
100
+ }
101
+ // 4. Re-derive the TxPoW ID from the mined header and check the target
102
+ const headerBytes = hexToBytes(proof.txpow);
103
+ const txpowId = (0, core_1.sha3_256)(headerBytes);
104
+ const txpowIdHex = toHex(txpowId);
105
+ if (txpowIdHex !== proof.txpowId) {
106
+ return { valid: false, reason: 'txpowId does not match mined header' };
107
+ }
108
+ const admissionTarget = hexToBytes(proof.admissionTarget);
109
+ if (!isLessThan(txpowId, admissionTarget)) {
110
+ return { valid: false, reason: 'txpowId does not beat admission target' };
111
+ }
112
+ // 5. Resolve a trusted template. Template fields — notably `blockDifficulty`,
113
+ // which drives the Super-level claim — must come from a freshly-fetched
114
+ // trusted template, never from the prover's copy (RFC-020 H4).
115
+ let trustedTemplate = options?.latestTemplate ?? null;
116
+ if (templateProvider) {
117
+ if (templateProvider.getLatestTemplate) {
118
+ trustedTemplate = await templateProvider.getLatestTemplate();
119
+ }
120
+ else if (options?.latestTemplate) {
121
+ trustedTemplate = options.latestTemplate;
122
+ }
123
+ else {
124
+ trustedTemplate = await templateProvider.getCurrentTemplate();
125
+ }
126
+ }
127
+ // 5a. Template freshness for admission (uses the proof's capture time).
128
+ const freshness = (0, template_js_1.templateFreshness)(proof.template, trustedTemplate, {
129
+ admissionWindowMs: options?.admissionWindowMs,
130
+ now: options?.now,
131
+ });
132
+ if (!freshness.admissionValid) {
133
+ return { valid: false, reason: 'proof template is stale for admission' };
134
+ }
135
+ // 5b. The prover's declared block difficulty must byte-match the trusted
136
+ // template — otherwise a prover could set a max difficulty to fake a
137
+ // Minima block.
138
+ if (trustedTemplate && trustedTemplate.blockDifficulty !== proof.template.blockDifficulty) {
139
+ return { valid: false, reason: 'proof template blockDifficulty does not match the trusted template' };
140
+ }
141
+ // 6. Level B: recompute the exact Minima Super level from the TRUSTED block
142
+ // difficulty and the re-derived txpowId. Without a trusted template we
143
+ // cannot claim Minima block contribution (isBlock = false).
144
+ const superLevel = trustedTemplate ? (0, template_js_1.computeSuperLevel)(txpowId, trustedTemplate.blockDifficulty) : -1;
145
+ const isBlock = trustedTemplate ? superLevel >= 0 : false;
146
+ // 7. Level C: broadcastability requires a live template provider, a current
147
+ // template, and isBlock. Offline mode leaves broadcastable undefined.
148
+ let broadcastable;
149
+ if (templateProvider) {
150
+ broadcastable = isBlock && trustedTemplate !== null && freshness.broadcastable;
151
+ }
152
+ return { valid: true, superLevel, isBlock, broadcastable };
153
+ }
154
+ function toHex(bytes) {
155
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
156
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * calibrate.ts — Hash rate measurement and mining cost estimation.
3
+ *
4
+ * calibrateHashRate() measures how many SHA3-256 hashes/sec the current
5
+ * environment can produce. Used by the extension UX to show a warning
6
+ * before starting a mine on slow devices.
7
+ *
8
+ * estimateMiningCost() converts a difficulty target + hash rate into an
9
+ * expected time and confidence label.
10
+ */
11
+ export interface MiningEstimate {
12
+ expectedHashes: bigint;
13
+ expectedSeconds: number;
14
+ confidence: 'fast' | 'normal' | 'slow';
15
+ }
16
+ /**
17
+ * Run 100K trial SHA3-256 hashes and return the measured hash rate (hashes/sec).
18
+ *
19
+ * Call once per session and cache the result — the cost is ~50ms on a
20
+ * typical desktop and ~300ms on a mid-range mobile.
21
+ */
22
+ export declare function calibrateHashRate(): Promise<number>;
23
+ /**
24
+ * Given a difficulty target and a measured hash rate, return the expected
25
+ * number of hashes, expected wall-clock time, and a confidence label.
26
+ *
27
+ * expectedHashes ≈ MAX_HASH / txnDifficulty
28
+ * (geometric distribution: each hash has P(valid) = txnDifficulty / MAX_HASH)
29
+ *
30
+ * Confidence labels:
31
+ * fast < 2 s
32
+ * normal 2 – 15 s
33
+ * slow > 15 s
34
+ */
35
+ export declare function estimateMiningCost(txnDifficulty: Uint8Array, hashRatePerSec: number): MiningEstimate;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * calibrate.ts — Hash rate measurement and mining cost estimation.
4
+ *
5
+ * calibrateHashRate() measures how many SHA3-256 hashes/sec the current
6
+ * environment can produce. Used by the extension UX to show a warning
7
+ * before starting a mine on slow devices.
8
+ *
9
+ * estimateMiningCost() converts a difficulty target + hash rate into an
10
+ * expected time and confidence label.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.calibrateHashRate = calibrateHashRate;
14
+ exports.estimateMiningCost = estimateMiningCost;
15
+ const core_1 = require("@totemsdk/core");
16
+ /**
17
+ * Run 100K trial SHA3-256 hashes and return the measured hash rate (hashes/sec).
18
+ *
19
+ * Call once per session and cache the result — the cost is ~50ms on a
20
+ * typical desktop and ~300ms on a mid-range mobile.
21
+ */
22
+ async function calibrateHashRate() {
23
+ const TRIAL_COUNT = 100000;
24
+ const data = new Uint8Array(150).fill(0xab);
25
+ const start = typeof performance !== 'undefined'
26
+ ? performance.now()
27
+ : Date.now();
28
+ for (let i = 0; i < TRIAL_COUNT; i++) {
29
+ (0, core_1.sha3_256)(data);
30
+ }
31
+ const elapsed = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - start;
32
+ return Math.round(TRIAL_COUNT / (elapsed / 1000));
33
+ }
34
+ /**
35
+ * Given a difficulty target and a measured hash rate, return the expected
36
+ * number of hashes, expected wall-clock time, and a confidence label.
37
+ *
38
+ * expectedHashes ≈ MAX_HASH / txnDifficulty
39
+ * (geometric distribution: each hash has P(valid) = txnDifficulty / MAX_HASH)
40
+ *
41
+ * Confidence labels:
42
+ * fast < 2 s
43
+ * normal 2 – 15 s
44
+ * slow > 15 s
45
+ */
46
+ function estimateMiningCost(txnDifficulty, hashRatePerSec) {
47
+ if (hashRatePerSec <= 0) {
48
+ throw new Error('hashRatePerSec must be positive');
49
+ }
50
+ const diffBigInt = txnDifficulty.reduce((acc, b) => (acc << 8n) | BigInt(b), 0n);
51
+ if (diffBigInt === 0n) {
52
+ return { expectedHashes: 0n, expectedSeconds: 0, confidence: 'fast' };
53
+ }
54
+ const MAX_HASH_BIGINT = (2n ** 256n) - 1n;
55
+ const expectedHashes = MAX_HASH_BIGINT / diffBigInt;
56
+ const expectedSeconds = Number(expectedHashes) / hashRatePerSec;
57
+ let confidence;
58
+ if (expectedSeconds < 2) {
59
+ confidence = 'fast';
60
+ }
61
+ else if (expectedSeconds < 15) {
62
+ confidence = 'normal';
63
+ }
64
+ else {
65
+ confidence = 'slow';
66
+ }
67
+ return { expectedHashes, expectedSeconds, confidence };
68
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * constants.ts — TxPoW-level constants matching Minima Java protocol values.
3
+ *
4
+ * ZERO_HASH: 32-byte all-zeros — used for MMRRoot, CustomHash, super-parent slot.
5
+ * MAX_HASH: 32-byte all-0xFF — default mBlockDifficulty / mTxnDifficulty for
6
+ * MEG-side-mined paths. Rejected by checkTxPoWSimple() at block level.
7
+ * TX_POW_MIN_DIFFICULTY: Safe transaction difficulty target.
8
+ * ≈ MAX_HASH / 1,000,000 = Magic.getMinTxPowWork() floor.
9
+ * Local-mining paths MUST use a target ≤ this value.
10
+ * MAIN_NET_CHAIN_ID: 1-byte [0x00] — Java MiniData("0x00") = MAIN_NET chain ID.
11
+ * CASCADE_LEVELS: 32 — number of super-parent slots in a fresh TxPoW header.
12
+ */
13
+ export declare const CASCADE_LEVELS = 32;
14
+ export declare const ZERO_HASH: Uint8Array<ArrayBuffer>;
15
+ export declare const MAX_HASH: Uint8Array;
16
+ /**
17
+ * TX_POW_MIN_DIFFICULTY = floor((2^256 - 1) / 1_000_000)
18
+ *
19
+ * This is the hardcoded floor constant matching Magic.getMinTxPowWork().
20
+ * Any locally mined TxPoW must have mTxnDifficulty ≤ this value to pass
21
+ * TxPoWChecker.checkTxPoWSimple() at block inclusion.
22
+ *
23
+ * Computed: (2n**256n - 1n) / 1_000_000n
24
+ * Hex: 0x000010C6F7A0B5ED8538AACDD46595F0C7AC73E0E9DBF12F70000000000000000
25
+ */
26
+ export declare const TX_POW_MIN_DIFFICULTY: Uint8Array;
27
+ export declare const MAIN_NET_CHAIN_ID: Uint8Array<ArrayBuffer>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * constants.ts — TxPoW-level constants matching Minima Java protocol values.
4
+ *
5
+ * ZERO_HASH: 32-byte all-zeros — used for MMRRoot, CustomHash, super-parent slot.
6
+ * MAX_HASH: 32-byte all-0xFF — default mBlockDifficulty / mTxnDifficulty for
7
+ * MEG-side-mined paths. Rejected by checkTxPoWSimple() at block level.
8
+ * TX_POW_MIN_DIFFICULTY: Safe transaction difficulty target.
9
+ * ≈ MAX_HASH / 1,000,000 = Magic.getMinTxPowWork() floor.
10
+ * Local-mining paths MUST use a target ≤ this value.
11
+ * MAIN_NET_CHAIN_ID: 1-byte [0x00] — Java MiniData("0x00") = MAIN_NET chain ID.
12
+ * CASCADE_LEVELS: 32 — number of super-parent slots in a fresh TxPoW header.
13
+ */
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 = (() => {
19
+ const b = new Uint8Array(32);
20
+ b.fill(0xff);
21
+ return b;
22
+ })();
23
+ /**
24
+ * TX_POW_MIN_DIFFICULTY = floor((2^256 - 1) / 1_000_000)
25
+ *
26
+ * This is the hardcoded floor constant matching Magic.getMinTxPowWork().
27
+ * Any locally mined TxPoW must have mTxnDifficulty ≤ this value to pass
28
+ * TxPoWChecker.checkTxPoWSimple() at block inclusion.
29
+ *
30
+ * Computed: (2n**256n - 1n) / 1_000_000n
31
+ * Hex: 0x000010C6F7A0B5ED8538AACDD46595F0C7AC73E0E9DBF12F70000000000000000
32
+ */
33
+ exports.TX_POW_MIN_DIFFICULTY = (() => {
34
+ const maxHashBigInt = (2n ** 256n) - 1n;
35
+ const minDiffBigInt = maxHashBigInt / 1000000n;
36
+ let hex = minDiffBigInt.toString(16);
37
+ if (hex.length % 2 !== 0)
38
+ hex = '0' + hex;
39
+ while (hex.length < 64)
40
+ hex = '00' + hex;
41
+ const bytes = new Uint8Array(32);
42
+ for (let i = 0; i < 32; i++) {
43
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
44
+ }
45
+ return bytes;
46
+ })();
47
+ exports.MAIN_NET_CHAIN_ID = new Uint8Array([0x00]);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * difficulty.ts — Fetch and manage the TxPoW difficulty target.
3
+ *
4
+ * The live value comes from `GET /v1/wallet/txpow-params` on the Axia API.
5
+ * On any error (timeout, network failure, invalid response) we fall back to
6
+ * the hardcoded TX_POW_MIN_DIFFICULTY constant, which is the protocol floor.
7
+ */
8
+ export interface TxPowParams {
9
+ minTxPowWork: string;
10
+ }
11
+ /** Anything that can expose a node's live minimum TxPoW work (hex). */
12
+ export interface MinWorkSource {
13
+ getMinTxPowWork?(): Promise<string | null>;
14
+ }
15
+ /**
16
+ * Convert a live `currentmintxpowwork` value (32-byte hex target) into a mining
17
+ * target, applying the same 10% margin the node's own wallet uses
18
+ * (`minwork * 10 / 11`, see Java `TxPoWGenerator.generateTxPoW`).
19
+ *
20
+ * `TxPoWChecker.checkTxPoWSimple` rejects only when
21
+ * `txnDifficulty > magic.getMinTxPowWork()`, so a target that is harder than
22
+ * the live minimum is always accepted; the margin absorbs the tiny per-block
23
+ * Magic retarget.
24
+ */
25
+ export declare function targetFromMinWork(minWorkHex: string): Uint8Array;
26
+ /**
27
+ * Resolve the TxPoW mining target from a live node when it exposes
28
+ * `getMinTxPowWork()`, otherwise fall back to `TX_POW_MIN_DIFFICULTY`.
29
+ *
30
+ * Never throws: a node/protocol hiccup must not block transaction
31
+ * construction. The fallback is the protocol default target (MAX/1e6), which
32
+ * is harder than the network hard floor (`Magic.MIN_HASHES = 10000`).
33
+ */
34
+ export declare function resolveTxPowTarget(source?: MinWorkSource | null, fallback?: Uint8Array): Promise<Uint8Array>;
35
+ /**
36
+ * Fetch the current minimum TxPoW work from the Axia API.
37
+ *
38
+ * Falls back to TX_POW_MIN_DIFFICULTY (the hardcoded protocol floor) on any
39
+ * error: network failure, timeout, or malformed response.
40
+ *
41
+ * @param axiaBaseUrl Base URL of the Axia API, e.g. "https://api.axia.to"
42
+ * @param timeoutMs Request timeout in ms (default: 3000)
43
+ */
44
+ export declare function fetchTxPowTarget(axiaBaseUrl: string, timeoutMs?: number): Promise<Uint8Array>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * difficulty.ts — Fetch and manage the TxPoW difficulty target.
4
+ *
5
+ * The live value comes from `GET /v1/wallet/txpow-params` on the Axia API.
6
+ * On any error (timeout, network failure, invalid response) we fall back to
7
+ * the hardcoded TX_POW_MIN_DIFFICULTY constant, which is the protocol floor.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.targetFromMinWork = targetFromMinWork;
11
+ exports.resolveTxPowTarget = resolveTxPowTarget;
12
+ exports.fetchTxPowTarget = fetchTxPowTarget;
13
+ const constants_js_1 = require("./constants.js");
14
+ /**
15
+ * Convert a live `currentmintxpowwork` value (32-byte hex target) into a mining
16
+ * target, applying the same 10% margin the node's own wallet uses
17
+ * (`minwork * 10 / 11`, see Java `TxPoWGenerator.generateTxPoW`).
18
+ *
19
+ * `TxPoWChecker.checkTxPoWSimple` rejects only when
20
+ * `txnDifficulty > magic.getMinTxPowWork()`, so a target that is harder than
21
+ * the live minimum is always accepted; the margin absorbs the tiny per-block
22
+ * Magic retarget.
23
+ */
24
+ function targetFromMinWork(minWorkHex) {
25
+ const clean = minWorkHex.replace(/^0x/i, '').trim();
26
+ if (clean.length === 0 || clean.length > 64 || !/^[0-9a-fA-F]+$/.test(clean)) {
27
+ throw new Error(`Invalid minTxPowWork: ${minWorkHex}`);
28
+ }
29
+ const value = BigInt('0x' + clean);
30
+ const adjusted = (value * 10n) / 11n;
31
+ const target = adjusted > 0n ? adjusted : value;
32
+ let hex = target.toString(16);
33
+ if (hex.length % 2 !== 0)
34
+ hex = '0' + hex;
35
+ while (hex.length < 64)
36
+ hex = '0' + hex;
37
+ const out = new Uint8Array(32);
38
+ for (let i = 0; i < 32; i++) {
39
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
40
+ }
41
+ return out;
42
+ }
43
+ /**
44
+ * Resolve the TxPoW mining target from a live node when it exposes
45
+ * `getMinTxPowWork()`, otherwise fall back to `TX_POW_MIN_DIFFICULTY`.
46
+ *
47
+ * Never throws: a node/protocol hiccup must not block transaction
48
+ * construction. The fallback is the protocol default target (MAX/1e6), which
49
+ * is harder than the network hard floor (`Magic.MIN_HASHES = 10000`).
50
+ */
51
+ async function resolveTxPowTarget(source, fallback = constants_js_1.TX_POW_MIN_DIFFICULTY) {
52
+ try {
53
+ const hex = await source?.getMinTxPowWork?.();
54
+ if (hex) {
55
+ const target = targetFromMinWork(hex);
56
+ if (target.some((b) => b !== 0))
57
+ return target;
58
+ }
59
+ }
60
+ catch {
61
+ // fall through to the constant
62
+ }
63
+ return new Uint8Array(fallback);
64
+ }
65
+ function hexToBytes(hex) {
66
+ const h = hex.startsWith('0x') ? hex.slice(2) : hex;
67
+ const out = new Uint8Array(h.length / 2);
68
+ for (let i = 0; i < h.length; i += 2) {
69
+ out[i / 2] = parseInt(h.slice(i, i + 2), 16);
70
+ }
71
+ return out;
72
+ }
73
+ /**
74
+ * Fetch the current minimum TxPoW work from the Axia API.
75
+ *
76
+ * Falls back to TX_POW_MIN_DIFFICULTY (the hardcoded protocol floor) on any
77
+ * error: network failure, timeout, or malformed response.
78
+ *
79
+ * @param axiaBaseUrl Base URL of the Axia API, e.g. "https://api.axia.to"
80
+ * @param timeoutMs Request timeout in ms (default: 3000)
81
+ */
82
+ async function fetchTxPowTarget(axiaBaseUrl, timeoutMs = 3000) {
83
+ try {
84
+ const base = axiaBaseUrl.replace(/\/$/, '');
85
+ const url = `${base}/v1/wallet/txpow-params`;
86
+ const controller = new AbortController();
87
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
88
+ let response;
89
+ try {
90
+ response = await fetch(url, { signal: controller.signal });
91
+ }
92
+ finally {
93
+ clearTimeout(timer);
94
+ }
95
+ if (!response.ok) {
96
+ throw new Error(`HTTP ${response.status}`);
97
+ }
98
+ const data = (await response.json());
99
+ if (typeof data.minTxPowWork !== 'string') {
100
+ throw new Error('Missing minTxPowWork field');
101
+ }
102
+ const clean = data.minTxPowWork.replace('0x', '').replace(/\s/g, '');
103
+ if (clean.length !== 64) {
104
+ throw new Error(`Invalid minTxPowWork length: ${clean.length} (expected 64 hex chars)`);
105
+ }
106
+ return hexToBytes(clean);
107
+ }
108
+ catch {
109
+ return new Uint8Array(constants_js_1.TX_POW_MIN_DIFFICULTY);
110
+ }
111
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @totemsdk/txpow
3
+ *
4
+ * TxPoW envelope serialization and proof-of-work mining for the Minima protocol.
5
+ *
6
+ * USAGE — MEG-side mining (byte-identical to extension current behaviour):
7
+ * const txpow = serializeTxPoW(txBytes, witnessBytes);
8
+ * // Submit to Axia: node re-mines with correct difficulty
9
+ *
10
+ * USAGE — Local mining:
11
+ * const target = await fetchTxPowTarget(axiaBaseUrl);
12
+ * const txBody = serializeTxBody(txBytes, witnessBytes, { txnDifficulty: target });
13
+ * const result = await mineTxPoW(txBody, target);
14
+ * const txpow = concat(result.minedHeaderBytes, new Uint8Array([0x01]), txBody);
15
+ *
16
+ * USAGE — Verify (relay nodes):
17
+ * const check = verifyProofOfWork(txpowId, mTxnDifficulty);
18
+ * if (!check.valid) drop(check.reason);
19
+ */
20
+ export { serializeTxHeader, serializeTxBody, serializeTxPoW, computeTxPoWId, type TxHeaderOptions, type TxBodyOptions, type TxPoWOptions, } from './serialization.js';
21
+ export { MAX_HASH, ZERO_HASH, TX_POW_MIN_DIFFICULTY, CASCADE_LEVELS, MAIN_NET_CHAIN_ID, } from './constants.js';
22
+ export { serializeMagic } from './magic.js';
23
+ export { mineTxPoW, mineTxPoWInProcess, mineHeaderTail, buildHeaderTail, isLessThan, setBrowserWorkerUrl, type MineResult, type MineOptions, } from './mine.js';
24
+ export { setWasmUrl, getBrowserWasmUrl, isWasmAvailable } from './mine-wasm.js';
25
+ export { fetchTxPowTarget, targetFromMinWork, resolveTxPowTarget, type TxPowParams, type MinWorkSource, } from './difficulty.js';
26
+ export { verifyProofOfWork, verifyTxPoWWork, verifyTxPoWParts, type VerifyResult } from './verify.js';
27
+ export { calibrateHashRate, estimateMiningCost, type MiningEstimate, } from './calibrate.js';
28
+ export { createWorkChallenge, validateWorkChallenge, challengeFingerprint, canonicalChallenge, computeActionCommitment, canonicalAction, mineWorkAdmission, verifyWorkAdmission, buildBlockHeaderTail, serializeSuperParents, isBlockWinner, computeSuperLevel, templateFreshness, computeBlockCandidateId, buildEmptyBlockBody, buildEmptyTransactionBytes, buildEmptyWitnessBytes, buildEmptyBurnTxBytes, buildEmptyBurnWitnessBytes, assembleTxPoWEnvelope, reconstructTxPoWEnvelope, MACHINE_WORK_ADMISSION_VERSION, MACHINE_WORK_DOMAIN, DEFAULT_CHALLENGE_TTL_MS, MAX_CHALLENGE_TTL_MS, type WorkChallenge, type MachineWorkAction, type MinimaWorkTemplate, type MinimaWorkTemplateProvider, type MinimaWorkRelay, type MachineWorkAdmissionProof, type WorkAdmissionVerification, type MineWorkAdmissionOptions, type VerifyWorkAdmissionOptions, } from './admission/index.js';
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * @totemsdk/txpow
4
+ *
5
+ * TxPoW envelope serialization and proof-of-work mining for the Minima protocol.
6
+ *
7
+ * USAGE — MEG-side mining (byte-identical to extension current behaviour):
8
+ * const txpow = serializeTxPoW(txBytes, witnessBytes);
9
+ * // Submit to Axia: node re-mines with correct difficulty
10
+ *
11
+ * USAGE — Local mining:
12
+ * const target = await fetchTxPowTarget(axiaBaseUrl);
13
+ * const txBody = serializeTxBody(txBytes, witnessBytes, { txnDifficulty: target });
14
+ * const result = await mineTxPoW(txBody, target);
15
+ * const txpow = concat(result.minedHeaderBytes, new Uint8Array([0x01]), txBody);
16
+ *
17
+ * USAGE — Verify (relay nodes):
18
+ * const check = verifyProofOfWork(txpowId, mTxnDifficulty);
19
+ * if (!check.valid) drop(check.reason);
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.MACHINE_WORK_DOMAIN = exports.MACHINE_WORK_ADMISSION_VERSION = exports.reconstructTxPoWEnvelope = exports.assembleTxPoWEnvelope = exports.buildEmptyBurnWitnessBytes = exports.buildEmptyBurnTxBytes = exports.buildEmptyWitnessBytes = exports.buildEmptyTransactionBytes = exports.buildEmptyBlockBody = exports.computeBlockCandidateId = exports.templateFreshness = exports.computeSuperLevel = exports.isBlockWinner = exports.serializeSuperParents = exports.buildBlockHeaderTail = exports.verifyWorkAdmission = exports.mineWorkAdmission = exports.canonicalAction = exports.computeActionCommitment = exports.canonicalChallenge = exports.challengeFingerprint = exports.validateWorkChallenge = exports.createWorkChallenge = exports.estimateMiningCost = exports.calibrateHashRate = exports.verifyTxPoWParts = exports.verifyTxPoWWork = exports.verifyProofOfWork = exports.resolveTxPowTarget = exports.targetFromMinWork = exports.fetchTxPowTarget = exports.isWasmAvailable = exports.getBrowserWasmUrl = exports.setWasmUrl = exports.setBrowserWorkerUrl = exports.isLessThan = exports.buildHeaderTail = exports.mineHeaderTail = exports.mineTxPoWInProcess = 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
+ exports.MAX_CHALLENGE_TTL_MS = exports.DEFAULT_CHALLENGE_TTL_MS = void 0;
24
+ var serialization_js_1 = require("./serialization.js");
25
+ Object.defineProperty(exports, "serializeTxHeader", { enumerable: true, get: function () { return serialization_js_1.serializeTxHeader; } });
26
+ Object.defineProperty(exports, "serializeTxBody", { enumerable: true, get: function () { return serialization_js_1.serializeTxBody; } });
27
+ Object.defineProperty(exports, "serializeTxPoW", { enumerable: true, get: function () { return serialization_js_1.serializeTxPoW; } });
28
+ Object.defineProperty(exports, "computeTxPoWId", { enumerable: true, get: function () { return serialization_js_1.computeTxPoWId; } });
29
+ var constants_js_1 = require("./constants.js");
30
+ Object.defineProperty(exports, "MAX_HASH", { enumerable: true, get: function () { return constants_js_1.MAX_HASH; } });
31
+ Object.defineProperty(exports, "ZERO_HASH", { enumerable: true, get: function () { return constants_js_1.ZERO_HASH; } });
32
+ Object.defineProperty(exports, "TX_POW_MIN_DIFFICULTY", { enumerable: true, get: function () { return constants_js_1.TX_POW_MIN_DIFFICULTY; } });
33
+ Object.defineProperty(exports, "CASCADE_LEVELS", { enumerable: true, get: function () { return constants_js_1.CASCADE_LEVELS; } });
34
+ Object.defineProperty(exports, "MAIN_NET_CHAIN_ID", { enumerable: true, get: function () { return constants_js_1.MAIN_NET_CHAIN_ID; } });
35
+ var magic_js_1 = require("./magic.js");
36
+ Object.defineProperty(exports, "serializeMagic", { enumerable: true, get: function () { return magic_js_1.serializeMagic; } });
37
+ var mine_js_1 = require("./mine.js");
38
+ Object.defineProperty(exports, "mineTxPoW", { enumerable: true, get: function () { return mine_js_1.mineTxPoW; } });
39
+ Object.defineProperty(exports, "mineTxPoWInProcess", { enumerable: true, get: function () { return mine_js_1.mineTxPoWInProcess; } });
40
+ Object.defineProperty(exports, "mineHeaderTail", { enumerable: true, get: function () { return mine_js_1.mineHeaderTail; } });
41
+ Object.defineProperty(exports, "buildHeaderTail", { enumerable: true, get: function () { return mine_js_1.buildHeaderTail; } });
42
+ Object.defineProperty(exports, "isLessThan", { enumerable: true, get: function () { return mine_js_1.isLessThan; } });
43
+ Object.defineProperty(exports, "setBrowserWorkerUrl", { enumerable: true, get: function () { return mine_js_1.setBrowserWorkerUrl; } });
44
+ var mine_wasm_js_1 = require("./mine-wasm.js");
45
+ Object.defineProperty(exports, "setWasmUrl", { enumerable: true, get: function () { return mine_wasm_js_1.setWasmUrl; } });
46
+ Object.defineProperty(exports, "getBrowserWasmUrl", { enumerable: true, get: function () { return mine_wasm_js_1.getBrowserWasmUrl; } });
47
+ Object.defineProperty(exports, "isWasmAvailable", { enumerable: true, get: function () { return mine_wasm_js_1.isWasmAvailable; } });
48
+ var difficulty_js_1 = require("./difficulty.js");
49
+ Object.defineProperty(exports, "fetchTxPowTarget", { enumerable: true, get: function () { return difficulty_js_1.fetchTxPowTarget; } });
50
+ Object.defineProperty(exports, "targetFromMinWork", { enumerable: true, get: function () { return difficulty_js_1.targetFromMinWork; } });
51
+ Object.defineProperty(exports, "resolveTxPowTarget", { enumerable: true, get: function () { return difficulty_js_1.resolveTxPowTarget; } });
52
+ var verify_js_1 = require("./verify.js");
53
+ Object.defineProperty(exports, "verifyProofOfWork", { enumerable: true, get: function () { return verify_js_1.verifyProofOfWork; } });
54
+ Object.defineProperty(exports, "verifyTxPoWWork", { enumerable: true, get: function () { return verify_js_1.verifyTxPoWWork; } });
55
+ Object.defineProperty(exports, "verifyTxPoWParts", { enumerable: true, get: function () { return verify_js_1.verifyTxPoWParts; } });
56
+ var calibrate_js_1 = require("./calibrate.js");
57
+ Object.defineProperty(exports, "calibrateHashRate", { enumerable: true, get: function () { return calibrate_js_1.calibrateHashRate; } });
58
+ Object.defineProperty(exports, "estimateMiningCost", { enumerable: true, get: function () { return calibrate_js_1.estimateMiningCost; } });
59
+ // ── Machine Work Admission ──────────────────────────────────────────────────
60
+ // Receiver-controlled computational admission bound to a real Minima block
61
+ // candidate. The same nonce search that admits a machine action also searches
62
+ // for Minima blocks (Super-0 … Super-31).
63
+ var index_js_1 = require("./admission/index.js");
64
+ Object.defineProperty(exports, "createWorkChallenge", { enumerable: true, get: function () { return index_js_1.createWorkChallenge; } });
65
+ Object.defineProperty(exports, "validateWorkChallenge", { enumerable: true, get: function () { return index_js_1.validateWorkChallenge; } });
66
+ Object.defineProperty(exports, "challengeFingerprint", { enumerable: true, get: function () { return index_js_1.challengeFingerprint; } });
67
+ Object.defineProperty(exports, "canonicalChallenge", { enumerable: true, get: function () { return index_js_1.canonicalChallenge; } });
68
+ Object.defineProperty(exports, "computeActionCommitment", { enumerable: true, get: function () { return index_js_1.computeActionCommitment; } });
69
+ Object.defineProperty(exports, "canonicalAction", { enumerable: true, get: function () { return index_js_1.canonicalAction; } });
70
+ Object.defineProperty(exports, "mineWorkAdmission", { enumerable: true, get: function () { return index_js_1.mineWorkAdmission; } });
71
+ Object.defineProperty(exports, "verifyWorkAdmission", { enumerable: true, get: function () { return index_js_1.verifyWorkAdmission; } });
72
+ Object.defineProperty(exports, "buildBlockHeaderTail", { enumerable: true, get: function () { return index_js_1.buildBlockHeaderTail; } });
73
+ Object.defineProperty(exports, "serializeSuperParents", { enumerable: true, get: function () { return index_js_1.serializeSuperParents; } });
74
+ Object.defineProperty(exports, "isBlockWinner", { enumerable: true, get: function () { return index_js_1.isBlockWinner; } });
75
+ Object.defineProperty(exports, "computeSuperLevel", { enumerable: true, get: function () { return index_js_1.computeSuperLevel; } });
76
+ Object.defineProperty(exports, "templateFreshness", { enumerable: true, get: function () { return index_js_1.templateFreshness; } });
77
+ Object.defineProperty(exports, "computeBlockCandidateId", { enumerable: true, get: function () { return index_js_1.computeBlockCandidateId; } });
78
+ Object.defineProperty(exports, "buildEmptyBlockBody", { enumerable: true, get: function () { return index_js_1.buildEmptyBlockBody; } });
79
+ Object.defineProperty(exports, "buildEmptyTransactionBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyTransactionBytes; } });
80
+ Object.defineProperty(exports, "buildEmptyWitnessBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyWitnessBytes; } });
81
+ Object.defineProperty(exports, "buildEmptyBurnTxBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyBurnTxBytes; } });
82
+ Object.defineProperty(exports, "buildEmptyBurnWitnessBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyBurnWitnessBytes; } });
83
+ Object.defineProperty(exports, "assembleTxPoWEnvelope", { enumerable: true, get: function () { return index_js_1.assembleTxPoWEnvelope; } });
84
+ Object.defineProperty(exports, "reconstructTxPoWEnvelope", { enumerable: true, get: function () { return index_js_1.reconstructTxPoWEnvelope; } });
85
+ Object.defineProperty(exports, "MACHINE_WORK_ADMISSION_VERSION", { enumerable: true, get: function () { return index_js_1.MACHINE_WORK_ADMISSION_VERSION; } });
86
+ Object.defineProperty(exports, "MACHINE_WORK_DOMAIN", { enumerable: true, get: function () { return index_js_1.MACHINE_WORK_DOMAIN; } });
87
+ Object.defineProperty(exports, "DEFAULT_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return index_js_1.DEFAULT_CHALLENGE_TTL_MS; } });
88
+ Object.defineProperty(exports, "MAX_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return index_js_1.MAX_CHALLENGE_TTL_MS; } });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * magic.ts — Serialize the Minima Magic struct.
3
+ *
4
+ * Ported from the extension's MinimaTransactionBuilder.ts::serializeMagic().
5
+ * Matches Magic.writeDataStream() in Java exactly.
6
+ *
7
+ * Magic contains two copies of 4 fields (current + desired parameters):
8
+ * maxTxPoWSize (MiniNumber)
9
+ * maxKISSVMOps (MiniNumber)
10
+ * maxTxnPerBlock(MiniNumber)
11
+ * minTxPoWWork (MiniData — 32 bytes)
12
+ *
13
+ * Default values from Magic.java:
14
+ * maxTxPoWSize = 65536
15
+ * maxKISSVMOps = 1024
16
+ * maxTxnPerBlock = 256
17
+ * minTxPoWWork = MAX_HASH (all 0xFF, 32 bytes) — placeholder in fresh TxPoW
18
+ */
19
+ export declare function serializeMagic(): Uint8Array;