@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,66 @@
1
+ /**
2
+ * admission/challenge.ts — WorkChallenge creation and validation.
3
+ *
4
+ * The receiver controls the admission requirement. A challenge must be unique
5
+ * enough to prevent useful pre-mining, must expire, must bind to the intended
6
+ * receiver, and must bind to an application domain. Verification NEVER trusts
7
+ * sender-reported hardware speed — it only checks `hash < target`.
8
+ *
9
+ * ── AUTHENTICATION BOUNDARY ────────────────────────────────────────────────
10
+ * `validateWorkChallenge()` validates structural/freshness/domain/recipient
11
+ * properties but does NOT prove that the claimed recipient actually issued the
12
+ * challenge. A WorkChallenge is a plain data object; anyone can construct one
13
+ * claiming any recipient. Proving the issuer requires authenticating the
14
+ * enclosing message (e.g. a signed machine-to-machine protocol message in the
15
+ * Edge negotiation layer). @totemsdk/txpow deliberately does NOT implement a
16
+ * parallel identity/signature system — that belongs to the application layer.
17
+ * ───────────────────────────────────────────────────────────────────────────
18
+ */
19
+ import { type WorkChallenge } from './types.js';
20
+ /** Default challenge lifetime: 5 minutes. */
21
+ export declare const DEFAULT_CHALLENGE_TTL_MS: number;
22
+ /** Maximum challenge lifetime: 24 hours. */
23
+ export declare const MAX_CHALLENGE_TTL_MS: number;
24
+ /**
25
+ * Create a WorkChallenge.
26
+ *
27
+ * @param recipient The intended receiver the challenge binds to.
28
+ * @param domain Application domain (open-ended, e.g. "totem.compute.reserve").
29
+ * @param target Absolute 32-byte cryptographic target (hex).
30
+ * @param options Optional overrides (challengeId, nonce, ttl, issuedAt, network).
31
+ */
32
+ export declare function createWorkChallenge(recipient: string, domain: string, target: string, options?: {
33
+ challengeId?: string;
34
+ nonce?: string;
35
+ ttlMs?: number;
36
+ issuedAt?: number;
37
+ network?: string;
38
+ }): WorkChallenge;
39
+ /**
40
+ * Validate a WorkChallenge at verification time.
41
+ *
42
+ * Checks structural integrity, expiry, and that the challenge binds to the
43
+ * expected recipient/domain. Does NOT check the proof hash — that is the
44
+ * caller's job via verifyWorkAdmission.
45
+ */
46
+ export declare function validateWorkChallenge(challenge: WorkChallenge, expected?: {
47
+ recipient?: string;
48
+ domain?: string;
49
+ now?: number;
50
+ }): {
51
+ valid: boolean;
52
+ reason?: string;
53
+ };
54
+ /**
55
+ * Derive a stable challenge fingerprint (SHA3-256 of canonical challenge bytes).
56
+ * Useful for higher-level code that tracks already-consumed challenge IDs.
57
+ */
58
+ export declare function challengeFingerprint(challenge: WorkChallenge): string;
59
+ /**
60
+ * Canonical serialization of a WorkChallenge.
61
+ *
62
+ * Deterministic field ordering with length-prefixed strings so that no
63
+ * ambiguous concatenation is possible. Used for the commitment and for
64
+ * challenge fingerprints.
65
+ */
66
+ export declare function canonicalChallenge(challenge: WorkChallenge): string;
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * admission/challenge.ts — WorkChallenge creation and validation.
4
+ *
5
+ * The receiver controls the admission requirement. A challenge must be unique
6
+ * enough to prevent useful pre-mining, must expire, must bind to the intended
7
+ * receiver, and must bind to an application domain. Verification NEVER trusts
8
+ * sender-reported hardware speed — it only checks `hash < target`.
9
+ *
10
+ * ── AUTHENTICATION BOUNDARY ────────────────────────────────────────────────
11
+ * `validateWorkChallenge()` validates structural/freshness/domain/recipient
12
+ * properties but does NOT prove that the claimed recipient actually issued the
13
+ * challenge. A WorkChallenge is a plain data object; anyone can construct one
14
+ * claiming any recipient. Proving the issuer requires authenticating the
15
+ * enclosing message (e.g. a signed machine-to-machine protocol message in the
16
+ * Edge negotiation layer). @totemsdk/txpow deliberately does NOT implement a
17
+ * parallel identity/signature system — that belongs to the application layer.
18
+ * ───────────────────────────────────────────────────────────────────────────
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.MAX_CHALLENGE_TTL_MS = exports.DEFAULT_CHALLENGE_TTL_MS = void 0;
22
+ exports.createWorkChallenge = createWorkChallenge;
23
+ exports.validateWorkChallenge = validateWorkChallenge;
24
+ exports.challengeFingerprint = challengeFingerprint;
25
+ exports.canonicalChallenge = canonicalChallenge;
26
+ const core_1 = require("@totemsdk/core");
27
+ const types_js_1 = require("./types.js");
28
+ /** Default challenge lifetime: 5 minutes. */
29
+ exports.DEFAULT_CHALLENGE_TTL_MS = 5 * 60 * 1000;
30
+ /** Maximum challenge lifetime: 24 hours. */
31
+ exports.MAX_CHALLENGE_TTL_MS = 24 * 60 * 60 * 1000;
32
+ function toHex(bytes) {
33
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
34
+ }
35
+ function randomHex(bytes) {
36
+ const out = new Uint8Array(bytes);
37
+ if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
38
+ throw new Error('SECURITY: crypto.getRandomValues unavailable — cannot generate secure random challenge');
39
+ }
40
+ crypto.getRandomValues(out);
41
+ return toHex(out);
42
+ }
43
+ /**
44
+ * Create a WorkChallenge.
45
+ *
46
+ * @param recipient The intended receiver the challenge binds to.
47
+ * @param domain Application domain (open-ended, e.g. "totem.compute.reserve").
48
+ * @param target Absolute 32-byte cryptographic target (hex).
49
+ * @param options Optional overrides (challengeId, nonce, ttl, issuedAt, network).
50
+ */
51
+ function createWorkChallenge(recipient, domain, target, options) {
52
+ if (!recipient)
53
+ throw new Error('WorkChallenge requires a recipient');
54
+ if (!domain)
55
+ throw new Error('WorkChallenge requires a domain');
56
+ if (!/^[0-9a-fA-F]{64}$/.test(target)) {
57
+ throw new Error('WorkChallenge target must be 32-byte hex (64 hex chars)');
58
+ }
59
+ const ttlMs = options?.ttlMs ?? exports.DEFAULT_CHALLENGE_TTL_MS;
60
+ if (ttlMs <= 0 || ttlMs > exports.MAX_CHALLENGE_TTL_MS) {
61
+ throw new Error(`WorkChallenge ttlMs must be in (0, ${exports.MAX_CHALLENGE_TTL_MS}]`);
62
+ }
63
+ const issuedAt = options?.issuedAt ?? Date.now();
64
+ const challengeId = options?.challengeId ?? randomHex(16);
65
+ const nonce = options?.nonce ?? randomHex(16);
66
+ return {
67
+ version: types_js_1.MACHINE_WORK_ADMISSION_VERSION,
68
+ challengeId,
69
+ recipient,
70
+ domain,
71
+ target,
72
+ nonce,
73
+ issuedAt,
74
+ expiresAt: issuedAt + ttlMs,
75
+ network: options?.network,
76
+ };
77
+ }
78
+ /**
79
+ * Validate a WorkChallenge at verification time.
80
+ *
81
+ * Checks structural integrity, expiry, and that the challenge binds to the
82
+ * expected recipient/domain. Does NOT check the proof hash — that is the
83
+ * caller's job via verifyWorkAdmission.
84
+ */
85
+ function validateWorkChallenge(challenge, expected) {
86
+ if (!challenge || typeof challenge !== 'object') {
87
+ return { valid: false, reason: 'challenge is not an object' };
88
+ }
89
+ if (challenge.version !== types_js_1.MACHINE_WORK_ADMISSION_VERSION) {
90
+ return { valid: false, reason: `unsupported challenge version ${challenge.version}` };
91
+ }
92
+ if (!challenge.challengeId) {
93
+ return { valid: false, reason: 'challenge is missing challengeId' };
94
+ }
95
+ if (!challenge.recipient) {
96
+ return { valid: false, reason: 'challenge is missing recipient' };
97
+ }
98
+ if (!challenge.domain) {
99
+ return { valid: false, reason: 'challenge is missing domain' };
100
+ }
101
+ if (!/^[0-9a-fA-F]{64}$/.test(challenge.target)) {
102
+ return { valid: false, reason: 'challenge target must be 32-byte hex' };
103
+ }
104
+ if (typeof challenge.issuedAt !== 'number' || typeof challenge.expiresAt !== 'number') {
105
+ return { valid: false, reason: 'challenge issuedAt/expiresAt must be numbers' };
106
+ }
107
+ const now = expected?.now ?? Date.now();
108
+ if (now > challenge.expiresAt) {
109
+ return { valid: false, reason: 'challenge has expired' };
110
+ }
111
+ if (now < challenge.issuedAt - 60000) {
112
+ return { valid: false, reason: 'challenge issuedAt is in the future' };
113
+ }
114
+ if (expected?.recipient && challenge.recipient !== expected.recipient) {
115
+ return { valid: false, reason: 'challenge recipient mismatch' };
116
+ }
117
+ if (expected?.domain && challenge.domain !== expected.domain) {
118
+ return { valid: false, reason: 'challenge domain mismatch' };
119
+ }
120
+ return { valid: true };
121
+ }
122
+ /**
123
+ * Derive a stable challenge fingerprint (SHA3-256 of canonical challenge bytes).
124
+ * Useful for higher-level code that tracks already-consumed challenge IDs.
125
+ */
126
+ function challengeFingerprint(challenge) {
127
+ const canonical = canonicalChallenge(challenge);
128
+ return toHex((0, core_1.sha3_256)(new TextEncoder().encode(canonical)));
129
+ }
130
+ /**
131
+ * Canonical serialization of a WorkChallenge.
132
+ *
133
+ * Deterministic field ordering with length-prefixed strings so that no
134
+ * ambiguous concatenation is possible. Used for the commitment and for
135
+ * challenge fingerprints.
136
+ */
137
+ function canonicalChallenge(challenge) {
138
+ const parts = [];
139
+ const push = (s) => {
140
+ parts.push(String(s.length).padStart(8, '0'));
141
+ parts.push(s);
142
+ };
143
+ push('totem.work-challenge');
144
+ push(String(challenge.version));
145
+ push(challenge.challengeId);
146
+ push(challenge.recipient);
147
+ push(challenge.domain);
148
+ push(challenge.target);
149
+ push(challenge.nonce);
150
+ push(String(challenge.issuedAt));
151
+ push(String(challenge.expiresAt));
152
+ if (challenge.network !== undefined)
153
+ push(challenge.network);
154
+ return parts.join('');
155
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * admission/commitment.ts — Canonical domain-separated action commitment.
3
+ *
4
+ * The commitment is a deterministic SHA3-256 over a length-prefixed canonical
5
+ * encoding of every security-relevant field. It changes if any of the
6
+ * following change:
7
+ * - protocol domain / version
8
+ * - action domain, sender, recipient, actionId, payloadHash, context
9
+ * - challenge challengeId, nonce, target, expiresAt
10
+ *
11
+ * The resulting 32-byte commitment is placed in the TxPoW header's customHash
12
+ * field, so the mined nonce commits the work to sender, recipient, action,
13
+ * challenge, target, and freshness.
14
+ */
15
+ import { type MachineWorkAction, type WorkChallenge } from './types.js';
16
+ /**
17
+ * Canonical serialization of a MachineWorkAction.
18
+ *
19
+ * Length-prefixed fields in a fixed order; context keys are sorted so that
20
+ * equivalent input produces identical output.
21
+ */
22
+ export declare function canonicalAction(action: MachineWorkAction): string;
23
+ /**
24
+ * Compute the canonical action commitment.
25
+ *
26
+ * commitment = SHA3-256(
27
+ * protocolDomain || protocolVersion ||
28
+ * canonicalAction(action) || canonicalChallenge(challenge)
29
+ * )
30
+ *
31
+ * @param action The application action.
32
+ * @param challenge The challenge the work is bound to.
33
+ */
34
+ export declare function computeActionCommitment(action: MachineWorkAction, challenge: WorkChallenge): string;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /**
3
+ * admission/commitment.ts — Canonical domain-separated action commitment.
4
+ *
5
+ * The commitment is a deterministic SHA3-256 over a length-prefixed canonical
6
+ * encoding of every security-relevant field. It changes if any of the
7
+ * following change:
8
+ * - protocol domain / version
9
+ * - action domain, sender, recipient, actionId, payloadHash, context
10
+ * - challenge challengeId, nonce, target, expiresAt
11
+ *
12
+ * The resulting 32-byte commitment is placed in the TxPoW header's customHash
13
+ * field, so the mined nonce commits the work to sender, recipient, action,
14
+ * challenge, target, and freshness.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.canonicalAction = canonicalAction;
18
+ exports.computeActionCommitment = computeActionCommitment;
19
+ const core_1 = require("@totemsdk/core");
20
+ const types_js_1 = require("./types.js");
21
+ const challenge_js_1 = require("./challenge.js");
22
+ function toHex(bytes) {
23
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
24
+ }
25
+ /**
26
+ * Canonical serialization of a MachineWorkAction.
27
+ *
28
+ * Length-prefixed fields in a fixed order; context keys are sorted so that
29
+ * equivalent input produces identical output.
30
+ */
31
+ function canonicalAction(action) {
32
+ const parts = [];
33
+ const push = (s) => {
34
+ parts.push(String(s.length).padStart(8, '0'));
35
+ parts.push(s);
36
+ };
37
+ push('totem.machine-work-action');
38
+ push(String(action.version));
39
+ push(action.domain);
40
+ push(action.sender);
41
+ push(action.recipient);
42
+ push(action.actionId);
43
+ push(action.payloadHash);
44
+ if (action.context) {
45
+ const keys = Object.keys(action.context).sort();
46
+ for (const key of keys) {
47
+ push(key);
48
+ push(action.context[key]);
49
+ }
50
+ }
51
+ return parts.join('');
52
+ }
53
+ /**
54
+ * Compute the canonical action commitment.
55
+ *
56
+ * commitment = SHA3-256(
57
+ * protocolDomain || protocolVersion ||
58
+ * canonicalAction(action) || canonicalChallenge(challenge)
59
+ * )
60
+ *
61
+ * @param action The application action.
62
+ * @param challenge The challenge the work is bound to.
63
+ */
64
+ function computeActionCommitment(action, challenge) {
65
+ const input = types_js_1.MACHINE_WORK_DOMAIN +
66
+ '|' +
67
+ String(types_js_1.MACHINE_WORK_ADMISSION_VERSION) +
68
+ '|' +
69
+ canonicalAction(action) +
70
+ '|' +
71
+ (0, challenge_js_1.canonicalChallenge)(challenge);
72
+ return toHex((0, core_1.sha3_256)(new TextEncoder().encode(input)));
73
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * admission/index.ts — Machine Work Admission public surface.
3
+ *
4
+ * Machine Work Admission allows applications to require computational proof
5
+ * before allocating scarce resources. The work is performed against a Minima
6
+ * block candidate so that application anti-spam work simultaneously searches
7
+ * for valid Minima L1 blocks.
8
+ */
9
+ export { createWorkChallenge, validateWorkChallenge, challengeFingerprint, canonicalChallenge, DEFAULT_CHALLENGE_TTL_MS, MAX_CHALLENGE_TTL_MS, } from './challenge.js';
10
+ export { computeActionCommitment, canonicalAction, } from './commitment.js';
11
+ export { mineWorkAdmission, type MineWorkAdmissionOptions, } from './mine.js';
12
+ export { verifyWorkAdmission, type VerifyWorkAdmissionOptions, } from './verify.js';
13
+ export { buildBlockHeaderTail, serializeSuperParents, isBlockWinner, computeSuperLevel, templateFreshness, computeBlockCandidateId, buildEmptyBlockBody, buildEmptyTransactionBytes, buildEmptyWitnessBytes, buildEmptyBurnTxBytes, buildEmptyBurnWitnessBytes, assembleTxPoWEnvelope, reconstructTxPoWEnvelope, } from './template.js';
14
+ export { MACHINE_WORK_ADMISSION_VERSION, MACHINE_WORK_DOMAIN, type WorkChallenge, type MachineWorkAction, type MinimaWorkTemplate, type MinimaWorkTemplateProvider, type MinimaWorkRelay, type MachineWorkAdmissionProof, type WorkAdmissionVerification, } from './types.js';
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * admission/index.ts — Machine Work Admission public surface.
4
+ *
5
+ * Machine Work Admission allows applications to require computational proof
6
+ * before allocating scarce resources. The work is performed against a Minima
7
+ * block candidate so that application anti-spam work simultaneously searches
8
+ * for valid Minima L1 blocks.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ 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.MAX_CHALLENGE_TTL_MS = exports.DEFAULT_CHALLENGE_TTL_MS = exports.canonicalChallenge = exports.challengeFingerprint = exports.validateWorkChallenge = exports.createWorkChallenge = void 0;
12
+ var challenge_js_1 = require("./challenge.js");
13
+ Object.defineProperty(exports, "createWorkChallenge", { enumerable: true, get: function () { return challenge_js_1.createWorkChallenge; } });
14
+ Object.defineProperty(exports, "validateWorkChallenge", { enumerable: true, get: function () { return challenge_js_1.validateWorkChallenge; } });
15
+ Object.defineProperty(exports, "challengeFingerprint", { enumerable: true, get: function () { return challenge_js_1.challengeFingerprint; } });
16
+ Object.defineProperty(exports, "canonicalChallenge", { enumerable: true, get: function () { return challenge_js_1.canonicalChallenge; } });
17
+ Object.defineProperty(exports, "DEFAULT_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return challenge_js_1.DEFAULT_CHALLENGE_TTL_MS; } });
18
+ Object.defineProperty(exports, "MAX_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return challenge_js_1.MAX_CHALLENGE_TTL_MS; } });
19
+ var commitment_js_1 = require("./commitment.js");
20
+ Object.defineProperty(exports, "computeActionCommitment", { enumerable: true, get: function () { return commitment_js_1.computeActionCommitment; } });
21
+ Object.defineProperty(exports, "canonicalAction", { enumerable: true, get: function () { return commitment_js_1.canonicalAction; } });
22
+ var mine_js_1 = require("./mine.js");
23
+ Object.defineProperty(exports, "mineWorkAdmission", { enumerable: true, get: function () { return mine_js_1.mineWorkAdmission; } });
24
+ var verify_js_1 = require("./verify.js");
25
+ Object.defineProperty(exports, "verifyWorkAdmission", { enumerable: true, get: function () { return verify_js_1.verifyWorkAdmission; } });
26
+ var template_js_1 = require("./template.js");
27
+ Object.defineProperty(exports, "buildBlockHeaderTail", { enumerable: true, get: function () { return template_js_1.buildBlockHeaderTail; } });
28
+ Object.defineProperty(exports, "serializeSuperParents", { enumerable: true, get: function () { return template_js_1.serializeSuperParents; } });
29
+ Object.defineProperty(exports, "isBlockWinner", { enumerable: true, get: function () { return template_js_1.isBlockWinner; } });
30
+ Object.defineProperty(exports, "computeSuperLevel", { enumerable: true, get: function () { return template_js_1.computeSuperLevel; } });
31
+ Object.defineProperty(exports, "templateFreshness", { enumerable: true, get: function () { return template_js_1.templateFreshness; } });
32
+ Object.defineProperty(exports, "computeBlockCandidateId", { enumerable: true, get: function () { return template_js_1.computeBlockCandidateId; } });
33
+ Object.defineProperty(exports, "buildEmptyBlockBody", { enumerable: true, get: function () { return template_js_1.buildEmptyBlockBody; } });
34
+ Object.defineProperty(exports, "buildEmptyTransactionBytes", { enumerable: true, get: function () { return template_js_1.buildEmptyTransactionBytes; } });
35
+ Object.defineProperty(exports, "buildEmptyWitnessBytes", { enumerable: true, get: function () { return template_js_1.buildEmptyWitnessBytes; } });
36
+ Object.defineProperty(exports, "buildEmptyBurnTxBytes", { enumerable: true, get: function () { return template_js_1.buildEmptyBurnTxBytes; } });
37
+ Object.defineProperty(exports, "buildEmptyBurnWitnessBytes", { enumerable: true, get: function () { return template_js_1.buildEmptyBurnWitnessBytes; } });
38
+ Object.defineProperty(exports, "assembleTxPoWEnvelope", { enumerable: true, get: function () { return template_js_1.assembleTxPoWEnvelope; } });
39
+ Object.defineProperty(exports, "reconstructTxPoWEnvelope", { enumerable: true, get: function () { return template_js_1.reconstructTxPoWEnvelope; } });
40
+ var types_js_1 = require("./types.js");
41
+ Object.defineProperty(exports, "MACHINE_WORK_ADMISSION_VERSION", { enumerable: true, get: function () { return types_js_1.MACHINE_WORK_ADMISSION_VERSION; } });
42
+ Object.defineProperty(exports, "MACHINE_WORK_DOMAIN", { enumerable: true, get: function () { return types_js_1.MACHINE_WORK_DOMAIN; } });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * admission/mine.ts — Machine Work Admission mining.
3
+ *
4
+ * Mines a real Minima block candidate whose customHash commits to the machine
5
+ * action. The mining loop reuses the existing TxPoW mining loop
6
+ * (`mineHeaderTail` from ../mine.js) — the same SHA3-256 nonce search that
7
+ * Minima uses for transaction/block mining.
8
+ *
9
+ * The admission target is ALWAYS `challenge.target` — the challenge is the
10
+ * single source of truth. There is no separate admission target argument.
11
+ *
12
+ * Two thresholds:
13
+ * - admission target (receiver-chosen, easier): hash < challenge.target
14
+ * - L1 block target (current Minima block difficulty): hash < blockTarget
15
+ *
16
+ * The loop stops as soon as the admission target is satisfied, unless the same
17
+ * search already discovered a block-target winner. Ordinary admission proofs
18
+ * stay off-chain; only genuine L1 winners are broadcast via the injected
19
+ * template provider.
20
+ */
21
+ import { type MachineWorkAction, type MachineWorkAdmissionProof, type MinimaWorkRelay, type MinimaWorkTemplateProvider, type WorkChallenge } from './types.js';
22
+ export interface MineWorkAdmissionOptions {
23
+ /** AbortSignal — rejects the Promise when aborted. */
24
+ signal?: AbortSignal;
25
+ /** Hard cap on total iterations (default: unlimited). */
26
+ maxIterations?: number;
27
+ /** Hash iterations per async yield (default: 10_000). */
28
+ chunkSize?: number;
29
+ /** Override the template timeMilli for deterministic testing. */
30
+ timeMilli?: bigint;
31
+ /** Force the pure-JS mining path (for testing). */
32
+ forceJs?: boolean;
33
+ /** Skip the Node.js worker_threads Worker (for testing). */
34
+ _skipWorker?: boolean;
35
+ /**
36
+ * Deterministic 32-byte PRNG for the TxBody (testing only). When omitted a
37
+ * cryptographically random PRNG is generated.
38
+ */
39
+ prng?: Uint8Array;
40
+ /**
41
+ * Preferred Minima block relay boundary. When supplied, a genuine current
42
+ * Minima block is submitted through the relay exactly once. Falls back to
43
+ * the deprecated provider.broadcastBlockCandidate only when no relay is set.
44
+ */
45
+ relay?: MinimaWorkRelay;
46
+ }
47
+ /**
48
+ * Mine a Machine Work Admission proof.
49
+ *
50
+ * The admission target is derived from `challenge.target` — the challenge is
51
+ * the single authoritative source. The miner searches the nonce space of a
52
+ * real Minima block candidate (from the injected template provider) whose
53
+ * customHash commits to the action.
54
+ *
55
+ * @param action The application action.
56
+ * @param challenge The receiver-issued challenge (target is authoritative).
57
+ * @param templateProvider Injected provider for the current Minima template.
58
+ * @param options Mining options.
59
+ */
60
+ export declare function mineWorkAdmission(action: MachineWorkAction, challenge: WorkChallenge, templateProvider: MinimaWorkTemplateProvider, options?: MineWorkAdmissionOptions): Promise<MachineWorkAdmissionProof>;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * admission/mine.ts — Machine Work Admission mining.
4
+ *
5
+ * Mines a real Minima block candidate whose customHash commits to the machine
6
+ * action. The mining loop reuses the existing TxPoW mining loop
7
+ * (`mineHeaderTail` from ../mine.js) — the same SHA3-256 nonce search that
8
+ * Minima uses for transaction/block mining.
9
+ *
10
+ * The admission target is ALWAYS `challenge.target` — the challenge is the
11
+ * single source of truth. There is no separate admission target argument.
12
+ *
13
+ * Two thresholds:
14
+ * - admission target (receiver-chosen, easier): hash < challenge.target
15
+ * - L1 block target (current Minima block difficulty): hash < blockTarget
16
+ *
17
+ * The loop stops as soon as the admission target is satisfied, unless the same
18
+ * search already discovered a block-target winner. Ordinary admission proofs
19
+ * stay off-chain; only genuine L1 winners are broadcast via the injected
20
+ * template provider.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.mineWorkAdmission = mineWorkAdmission;
24
+ const core_1 = require("@totemsdk/core");
25
+ const mine_js_1 = require("../mine.js");
26
+ const commitment_js_1 = require("./commitment.js");
27
+ const template_js_1 = require("./template.js");
28
+ const types_js_1 = require("./types.js");
29
+ function toHex(bytes) {
30
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
31
+ }
32
+ /**
33
+ * Mine a Machine Work Admission proof.
34
+ *
35
+ * The admission target is derived from `challenge.target` — the challenge is
36
+ * the single authoritative source. The miner searches the nonce space of a
37
+ * real Minima block candidate (from the injected template provider) whose
38
+ * customHash commits to the action.
39
+ *
40
+ * @param action The application action.
41
+ * @param challenge The receiver-issued challenge (target is authoritative).
42
+ * @param templateProvider Injected provider for the current Minima template.
43
+ * @param options Mining options.
44
+ */
45
+ async function mineWorkAdmission(action, challenge, templateProvider, options) {
46
+ const template = await templateProvider.getCurrentTemplate();
47
+ if (templateProvider.validateTemplate) {
48
+ const ok = await templateProvider.validateTemplate(template);
49
+ if (!ok)
50
+ throw new Error('Minima work template failed validation');
51
+ }
52
+ const actionCommitment = (0, commitment_js_1.computeActionCommitment)(action, challenge);
53
+ const admissionTarget = challenge.target;
54
+ const admissionTargetBytes = hexToBytes(admissionTarget);
55
+ const blockTargetBytes = hexToBytes(template.blockDifficulty);
56
+ // The admission target must be easier than or equal to the block target,
57
+ // otherwise the search would be chasing a block that can never admit.
58
+ if (!isLessThanOrEqual(blockTargetBytes, admissionTargetBytes)) {
59
+ throw new Error('challenge.target must be easier than or equal to the current block target');
60
+ }
61
+ // Build the complete block candidate: empty TxBody (same shape Minima's
62
+ // MINEPULSE automine uses) + header tail with the action commitment.
63
+ const prng = options?.prng ?? randomBytes(32);
64
+ const bodyBytes = (0, template_js_1.buildEmptyBlockBody)(prng, template.blockDifficulty);
65
+ const bodyHash = toHex((0, core_1.sha3_256)(bodyBytes));
66
+ const timeMilli = options?.timeMilli ?? template.timeMilli;
67
+ const headerTail = (0, template_js_1.buildBlockHeaderTail)(template, actionCommitment, bodyHash);
68
+ // Mine against the admission target. The same nonce search also checks the
69
+ // block target: if the winning hash beats the block target, it is a block.
70
+ const result = await (0, mine_js_1.mineHeaderTail)(headerTail, admissionTargetBytes, {
71
+ signal: options?.signal,
72
+ maxIterations: options?.maxIterations,
73
+ chunkSize: options?.chunkSize,
74
+ timeMilli,
75
+ forceJs: options?.forceJs,
76
+ _skipWorker: options?._skipWorker,
77
+ });
78
+ // Assemble the complete Minima TxPoW wire format (header | 0x01 | body).
79
+ const envelope = (0, template_js_1.assembleTxPoWEnvelope)(result.minedHeaderBytes, bodyBytes);
80
+ // Exact Minima-derived metadata.
81
+ const superLevel = (0, template_js_1.computeSuperLevel)(result.txpowId, template.blockDifficulty);
82
+ const isBlock = superLevel >= 0;
83
+ const proof = {
84
+ version: types_js_1.MACHINE_WORK_ADMISSION_VERSION,
85
+ actionCommitment,
86
+ challengeId: challenge.challengeId,
87
+ admissionTarget,
88
+ txpow: toHex(result.minedHeaderBytes),
89
+ txpowEnvelope: toHex(envelope),
90
+ txpowId: toHex(result.txpowId),
91
+ nonce: result.nonce.toString(),
92
+ minedAt: Date.now(),
93
+ qualifiesForAdmission: true,
94
+ qualifiesAsMinimaBlock: isBlock,
95
+ superLevel,
96
+ isBlock,
97
+ template,
98
+ };
99
+ // Broadcast genuine Minima blocks exactly once, and only if the template is
100
+ // still current. All Super levels (0..31) are eligible — Super level is
101
+ // block strength, NOT a relay policy. Ordinary admission proofs never broadcast.
102
+ if (isBlock) {
103
+ let latest;
104
+ if (templateProvider.getLatestTemplate) {
105
+ latest = await templateProvider.getLatestTemplate();
106
+ }
107
+ else {
108
+ latest = await templateProvider.getCurrentTemplate();
109
+ }
110
+ if (latest.templateId === template.templateId) {
111
+ if (options?.relay) {
112
+ await options.relay.submitBlock(envelope);
113
+ }
114
+ else if (templateProvider.broadcastBlockCandidate) {
115
+ await templateProvider.broadcastBlockCandidate(proof);
116
+ }
117
+ }
118
+ }
119
+ return proof;
120
+ }
121
+ function hexToBytes(hex) {
122
+ const h = hex.startsWith('0x') ? hex.slice(2) : hex;
123
+ const out = new Uint8Array(h.length / 2);
124
+ for (let i = 0; i < h.length; i += 2) {
125
+ out[i / 2] = parseInt(h.slice(i, i + 2), 16);
126
+ }
127
+ return out;
128
+ }
129
+ function randomBytes(length) {
130
+ const out = new Uint8Array(length);
131
+ if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
132
+ throw new Error('SECURITY: crypto.getRandomValues unavailable — cannot generate secure random PRNG');
133
+ }
134
+ crypto.getRandomValues(out);
135
+ return out;
136
+ }
137
+ /** Big-endian 256-bit comparison: true if a <= b. */
138
+ function isLessThanOrEqual(a, b) {
139
+ for (let i = 0; i < 32; i++) {
140
+ if (a[i] < b[i])
141
+ return true;
142
+ if (a[i] > b[i])
143
+ return false;
144
+ }
145
+ return true;
146
+ }