@totemsdk/txpow 1.0.0 → 1.0.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.
@@ -0,0 +1,224 @@
1
+ /**
2
+ * admission/types.ts — Machine Work Admission core types.
3
+ *
4
+ * Machine Work Admission lets a receiver require a sender to perform bounded
5
+ * computational work before scarce machine resources are allocated. The work
6
+ * searches the nonce space of a real current Minima block candidate, so the
7
+ * same hashes that admit the machine action also search for Minima L1 blocks.
8
+ *
9
+ * The three distinct concepts:
10
+ * - WorkChallenge — the receiver-controlled admission requirement
11
+ * - MachineWorkAction — the domain-separated application action commitment
12
+ * - MachineWorkAdmissionProof — the mined result
13
+ *
14
+ * The TxPoW package knows only "there is an application action represented by
15
+ * canonical bytes". It does NOT know about negotiation, compute, storage, or
16
+ * any particular Edge resource. Domain-specific packages supply the action.
17
+ */
18
+ /** Current Machine Work Admission protocol version. */
19
+ export declare const MACHINE_WORK_ADMISSION_VERSION = 1;
20
+ /** Canonical protocol domain prefix for the commitment hash. */
21
+ export declare const MACHINE_WORK_DOMAIN = "totem.machine-work-admission";
22
+ /**
23
+ * The receiver controls the admission requirement.
24
+ *
25
+ * A challenge must be unique enough to prevent useful pre-mining, must expire,
26
+ * must bind to the intended receiver, and must bind to an application domain.
27
+ * `target` is an absolute cryptographic target: verification is `hash < target`.
28
+ */
29
+ export interface WorkChallenge {
30
+ /** Protocol version. */
31
+ version: number;
32
+ /** Unique challenge identifier (receiver-generated, prevents pre-mining). */
33
+ challengeId: string;
34
+ /** The intended receiver this challenge is bound to. */
35
+ recipient: string;
36
+ /** Application domain (e.g. "totem.negotiation.proposal"). Open-ended. */
37
+ domain: string;
38
+ /** Absolute 32-byte cryptographic target (big-endian 256-bit). */
39
+ target: string;
40
+ /** Receiver-generated random nonce to prevent pre-mining. */
41
+ nonce: string;
42
+ /** Epoch milliseconds when the challenge was issued. */
43
+ issuedAt: number;
44
+ /** Epoch milliseconds after which the challenge is invalid. */
45
+ expiresAt: number;
46
+ /** Optional network identifier (e.g. "mainnet", "testnet"). */
47
+ network?: string;
48
+ }
49
+ /**
50
+ * The application action, represented in a generic domain-separated form.
51
+ *
52
+ * Only a commitment to this action enters the TxPoW header — never the
53
+ * application payload itself. The application remains off-chain.
54
+ */
55
+ export interface MachineWorkAction {
56
+ /** Protocol version. */
57
+ version: number;
58
+ /** Application domain (e.g. "totem.compute.reserve"). Open-ended. */
59
+ domain: string;
60
+ /** The sender performing the work. */
61
+ sender: string;
62
+ /** The intended recipient. */
63
+ recipient: string;
64
+ /** Unique action identifier. */
65
+ actionId: string;
66
+ /** SHA3-256 hex commitment to the application payload (off-chain). */
67
+ payloadHash: string;
68
+ /** Optional domain-specific context (canonicalized into the commitment). */
69
+ context?: Record<string, string>;
70
+ }
71
+ /**
72
+ * A Minima block-candidate work template.
73
+ *
74
+ * This is the chain state a candidate is mined against. It is structurally
75
+ * capable of becoming a legitimate Minima block: block number, block
76
+ * difficulty, super-parents, MMR root/total, magic, and time are all taken
77
+ * from the current chain tip (see TxPoWGenerator.generateTxPoW in Minima).
78
+ */
79
+ export interface MinimaWorkTemplate {
80
+ /** Chain ID (MAIN_NET = 0x00). */
81
+ chainId: string;
82
+ /** Block number = current tip + 1. */
83
+ blockNumber: bigint;
84
+ /** Current block difficulty target (32-byte hex). */
85
+ blockDifficulty: string;
86
+ /** Super-parent hashes at each cascade level (RLE-serialized). */
87
+ superParents: string[];
88
+ /** Current MMR root (32-byte hex). */
89
+ mmrRoot: string;
90
+ /** Current MMR total (sum of all coins). */
91
+ mmrTotal: bigint;
92
+ /** Serialized Magic struct (hex). */
93
+ magic: string;
94
+ /** Candidate timestamp in epoch milliseconds. */
95
+ timeMilli: bigint;
96
+ /** Template identifier (e.g. tip txpowId) for staleness checks. */
97
+ templateId: string;
98
+ /** Epoch milliseconds when the template was captured. */
99
+ capturedAt: number;
100
+ }
101
+ /**
102
+ * Provider abstraction for the current Minima block-candidate template.
103
+ *
104
+ * The TxPoW package remains transport/node-client agnostic: callers inject a
105
+ * provider that fetches the current template from a Minima node, Axia, or a
106
+ * test fixture.
107
+ *
108
+ * `MinimaWorkRelay` is the preferred long-term relay boundary. The legacy
109
+ * `broadcastBlockCandidate` callback is retained as a compatibility/fallback
110
+ * path only — new consumers should use `relay` via `MinimaWorkRelay` and not
111
+ * build around the lossy `candidate: unknown` shape.
112
+ */
113
+ export interface MinimaWorkTemplateProvider {
114
+ /** Fetch the current block-candidate template. */
115
+ getCurrentTemplate(): Promise<MinimaWorkTemplate>;
116
+ /** Optional: fetch the latest template for freshness checks. Falls back to getCurrentTemplate. */
117
+ getLatestTemplate?(): Promise<MinimaWorkTemplate>;
118
+ /** Optional: validate a template before mining against it. */
119
+ validateTemplate?(template: MinimaWorkTemplate): Promise<boolean>;
120
+ /**
121
+ * @deprecated Prefer `MinimaWorkRelay.submitBlock` (complete envelope).
122
+ * Retained for compatibility; only invoked for genuine Minima blocks
123
+ * (Super-0 … Super-31) when no relay is configured.
124
+ */
125
+ broadcastBlockCandidate?(candidate: MachineWorkAdmissionProof): Promise<void>;
126
+ }
127
+ /**
128
+ * Relay boundary for a complete Minima TxPoW envelope.
129
+ *
130
+ * Keeps Minima networking outside core mining logic. A future
131
+ * @totemsdk/minima-rpc or chain-provider adapter may implement this port.
132
+ * Duplicate relay attempts must be safe/idempotent at the integration boundary.
133
+ */
134
+ export interface MinimaWorkRelay {
135
+ /** Submit a complete Minima TxPoW envelope for block relay. */
136
+ submitBlock(envelope: Uint8Array): Promise<void>;
137
+ }
138
+ /**
139
+ * The mined Machine Work Admission proof.
140
+ *
141
+ * Reuses the existing TxPoW types where possible:
142
+ * - `txpow` — serialized TxHeader bytes (SHA3-256 of these is `txpowId`)
143
+ * - `txpowEnvelope` — the COMPLETE Minima TxPoW wire format
144
+ * (header | 0x01 hasBody | body), required for network
145
+ * submission of a genuine Minima block
146
+ * - `txpowId` — SHA3-256(header)
147
+ *
148
+ * `qualifiesAsMinimaBlock`, `isBlock`, and `superLevel` are DERIVED METADATA
149
+ * recorded at mining time. They are never trusted by verification —
150
+ * verification recomputes all of them from the re-derived txpowId and the
151
+ * template's block difficulty.
152
+ */
153
+ export interface MachineWorkAdmissionProof {
154
+ /** Protocol version. */
155
+ version: number;
156
+ /** Hex commitment placed in the TxPoW header's customHash field. */
157
+ actionCommitment: string;
158
+ /** The challenge this proof was mined against. */
159
+ challengeId: string;
160
+ /** The admission target (32-byte hex) the proof satisfies. */
161
+ admissionTarget: string;
162
+ /** Serialized TxHeader bytes (SHA3-256 of these is the TxPoW ID). */
163
+ txpow: string;
164
+ /** Complete Minima TxPoW envelope (header | 0x01 | body) for block relay. */
165
+ txpowEnvelope: string;
166
+ /** SHA3-256(header) — the canonical TxPoW ID. */
167
+ txpowId: string;
168
+ /** The winning nonce. */
169
+ nonce: string;
170
+ /** Epoch milliseconds when the proof was mined. */
171
+ minedAt: number;
172
+ /** Always true for a valid admission proof. */
173
+ qualifiesForAdmission: true;
174
+ /**
175
+ * DERIVED METADATA: true when the mined hash also beats the block difficulty
176
+ * encoded by the candidate template (i.e. a genuine Minima block). Never
177
+ * trusted by verification — it is recomputed from the re-derived txpowId.
178
+ */
179
+ qualifiesAsMinimaBlock: boolean;
180
+ /**
181
+ * DERIVED METADATA: Minima Super level (-1 = not a block, 0..31 = block
182
+ * strength). Never trusted by verification.
183
+ */
184
+ superLevel: number;
185
+ /** DERIVED METADATA: superLevel >= 0. Never trusted by verification. */
186
+ isBlock: boolean;
187
+ /** Template the proof was mined against (for staleness policy). */
188
+ template: MinimaWorkTemplate;
189
+ }
190
+ /**
191
+ * Result of verifying a Machine Work Admission proof.
192
+ *
193
+ * Three distinct claims, never to be confused:
194
+ * A. `valid` — the hash satisfies the challenge target (admission proof).
195
+ * B. `superLevel` / `isBlock` — the hash ALSO satisfies the block difficulty
196
+ * encoded by the candidate template, yielding an exact Minima Super level.
197
+ * C. `broadcastable` — the candidate still corresponds to sufficiently
198
+ * current live Minima state AND can be submitted through the relay.
199
+ *
200
+ * A stale candidate may remain `valid = true` (and `superLevel >= 0`) while
201
+ * `broadcastable = false`.
202
+ *
203
+ * `superLevel === -1 ⇔ isBlock === false`, and
204
+ * `superLevel >= 0 ⇔ isBlock === true`. Verification recomputes these — it
205
+ * never trusts sender-supplied `isBlock`/`superLevel` metadata.
206
+ */
207
+ export interface WorkAdmissionVerification {
208
+ /** Level A: the hash satisfies the challenge target. */
209
+ valid: boolean;
210
+ reason?: string;
211
+ /**
212
+ * Level B: exact Minima Super level of the candidate hash.
213
+ * -1 = not a Minima block; 0..31 = Super-0 … Super-31 block strength.
214
+ */
215
+ superLevel?: number;
216
+ /** Level B: superLevel >= 0 (a genuine Minima block). */
217
+ isBlock?: boolean;
218
+ /**
219
+ * Level C: isBlock AND the template is current AND a live template provider
220
+ * was supplied. Undefined in offline mode (no provider) — offline
221
+ * verification must NOT claim Minima block contribution.
222
+ */
223
+ broadcastable?: boolean;
224
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * admission/types.ts — Machine Work Admission core types.
4
+ *
5
+ * Machine Work Admission lets a receiver require a sender to perform bounded
6
+ * computational work before scarce machine resources are allocated. The work
7
+ * searches the nonce space of a real current Minima block candidate, so the
8
+ * same hashes that admit the machine action also search for Minima L1 blocks.
9
+ *
10
+ * The three distinct concepts:
11
+ * - WorkChallenge — the receiver-controlled admission requirement
12
+ * - MachineWorkAction — the domain-separated application action commitment
13
+ * - MachineWorkAdmissionProof — the mined result
14
+ *
15
+ * The TxPoW package knows only "there is an application action represented by
16
+ * canonical bytes". It does NOT know about negotiation, compute, storage, or
17
+ * any particular Edge resource. Domain-specific packages supply the action.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.MACHINE_WORK_DOMAIN = exports.MACHINE_WORK_ADMISSION_VERSION = void 0;
21
+ /** Current Machine Work Admission protocol version. */
22
+ exports.MACHINE_WORK_ADMISSION_VERSION = 1;
23
+ /** Canonical protocol domain prefix for the commitment hash. */
24
+ exports.MACHINE_WORK_DOMAIN = 'totem.machine-work-admission';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * admission/verify.ts — Machine Work Admission verification.
3
+ *
4
+ * Verification NEVER trusts sender-reported hardware speed or self-reported
5
+ * block metadata (`isBlock`, `superLevel`, `qualifiesAsMinimaBlock`,
6
+ * `broadcastable`). It recomputes the canonical action commitment, re-derives
7
+ * the TxPoW ID from the mined header, and checks `txpowId < challenge.target`.
8
+ * It also checks challenge validity (expiry, recipient, domain) and template
9
+ * freshness.
10
+ *
11
+ * Three distinct claims, never to be confused:
12
+ * A. valid — the hash satisfies the challenge target.
13
+ * B. superLevel/isBlock — the exact Minima Super level (-1 or 0..31)
14
+ * computed from blockDifficulty / txpowId.
15
+ * C. broadcastable — isBlock AND the template is current AND a live
16
+ * template provider was supplied.
17
+ *
18
+ * A stale candidate may remain valid = true (and superLevel >= 0) while
19
+ * broadcastable = false. Offline verification (no provider) must NOT claim
20
+ * Minima block contribution — `broadcastable` is left undefined.
21
+ */
22
+ import { type MachineWorkAction, type MachineWorkAdmissionProof, type MinimaWorkTemplate, type MinimaWorkTemplateProvider, type WorkChallenge, type WorkAdmissionVerification } from './types.js';
23
+ export interface VerifyWorkAdmissionOptions {
24
+ /** Override the current time for deterministic testing. */
25
+ now?: number;
26
+ /** Staleness window (ms) within which a template is acceptable for admission. */
27
+ admissionWindowMs?: number;
28
+ /** The latest template, for broadcastability checks. */
29
+ latestTemplate?: MinimaWorkTemplate | null;
30
+ }
31
+ /**
32
+ * Verify a Machine Work Admission proof.
33
+ *
34
+ * The admission target is taken from the validated challenge — never from the
35
+ * proof. `proof.qualifiesAsMinimaBlock`, `proof.superLevel`, and
36
+ * `proof.isBlock` are treated as derived metadata and are NOT trusted; the
37
+ * Super level is recomputed from the re-derived txpowId and the template's
38
+ * block difficulty.
39
+ *
40
+ * @param action The application action the proof claims to commit.
41
+ * @param challenge The challenge the proof claims to satisfy.
42
+ * @param proof The mined proof.
43
+ * @param templateProvider Optional live provider. When supplied, template
44
+ * freshness and broadcastability are checked and
45
+ * `broadcastable` is set. When omitted, verification
46
+ * runs in offline mode and does NOT claim Minima
47
+ * block contribution (`broadcastable` is undefined).
48
+ * @param options Verification options.
49
+ */
50
+ export declare function verifyWorkAdmission(action: MachineWorkAction, challenge: WorkChallenge, proof: MachineWorkAdmissionProof, templateProvider?: MinimaWorkTemplateProvider, options?: VerifyWorkAdmissionOptions): Promise<WorkAdmissionVerification>;
@@ -0,0 +1,144 @@
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. Template freshness for admission
113
+ const freshness = (0, template_js_1.templateFreshness)(proof.template, options?.latestTemplate ?? null, {
114
+ admissionWindowMs: options?.admissionWindowMs,
115
+ now: options?.now,
116
+ });
117
+ if (!freshness.admissionValid) {
118
+ return { valid: false, reason: 'proof template is stale for admission' };
119
+ }
120
+ // 6. Level B: independently recompute the exact Minima Super level. Never
121
+ // trust proof.superLevel / proof.isBlock / proof.qualifiesAsMinimaBlock.
122
+ const superLevel = (0, template_js_1.computeSuperLevel)(txpowId, proof.template.blockDifficulty);
123
+ const isBlock = superLevel >= 0;
124
+ // 7. Level C: broadcastability requires a live template provider AND a
125
+ // current template. Offline mode leaves broadcastable undefined.
126
+ let broadcastable;
127
+ if (templateProvider) {
128
+ let latest;
129
+ if (templateProvider.getLatestTemplate) {
130
+ latest = await templateProvider.getLatestTemplate();
131
+ }
132
+ else if (options?.latestTemplate) {
133
+ latest = options.latestTemplate;
134
+ }
135
+ else {
136
+ latest = await templateProvider.getCurrentTemplate();
137
+ }
138
+ broadcastable = isBlock && latest.templateId === proof.template.templateId;
139
+ }
140
+ return { valid: true, superLevel, isBlock, broadcastable };
141
+ }
142
+ function toHex(bytes) {
143
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
144
+ }
package/dist/index.d.ts CHANGED
@@ -20,8 +20,9 @@
20
20
  export { serializeTxHeader, serializeTxBody, serializeTxPoW, computeTxPoWId, type TxHeaderOptions, type TxBodyOptions, type TxPoWOptions, } from './serialization.js';
21
21
  export { MAX_HASH, ZERO_HASH, TX_POW_MIN_DIFFICULTY, CASCADE_LEVELS, MAIN_NET_CHAIN_ID, } from './constants.js';
22
22
  export { serializeMagic } from './magic.js';
23
- export { mineTxPoW, mineTxPoWInProcess, isLessThan, setBrowserWorkerUrl, type MineResult, type MineOptions, } from './mine.js';
23
+ export { mineTxPoW, mineTxPoWInProcess, mineHeaderTail, buildHeaderTail, isLessThan, setBrowserWorkerUrl, type MineResult, type MineOptions, } from './mine.js';
24
24
  export { setWasmUrl, getBrowserWasmUrl, isWasmAvailable } from './mine-wasm.js';
25
25
  export { fetchTxPowTarget, type TxPowParams } from './difficulty.js';
26
26
  export { verifyProofOfWork, verifyTxPoWWork, verifyTxPoWParts, type VerifyResult } from './verify.js';
27
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';
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * if (!check.valid) drop(check.reason);
20
20
  */
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.estimateMiningCost = exports.calibrateHashRate = exports.verifyTxPoWParts = exports.verifyTxPoWWork = exports.verifyProofOfWork = exports.fetchTxPowTarget = exports.isWasmAvailable = exports.getBrowserWasmUrl = exports.setWasmUrl = exports.setBrowserWorkerUrl = exports.isLessThan = 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;
22
+ exports.MAX_CHALLENGE_TTL_MS = exports.DEFAULT_CHALLENGE_TTL_MS = 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.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
23
  var serialization_js_1 = require("./serialization.js");
24
24
  Object.defineProperty(exports, "serializeTxHeader", { enumerable: true, get: function () { return serialization_js_1.serializeTxHeader; } });
25
25
  Object.defineProperty(exports, "serializeTxBody", { enumerable: true, get: function () { return serialization_js_1.serializeTxBody; } });
@@ -36,6 +36,8 @@ Object.defineProperty(exports, "serializeMagic", { enumerable: true, get: functi
36
36
  var mine_js_1 = require("./mine.js");
37
37
  Object.defineProperty(exports, "mineTxPoW", { enumerable: true, get: function () { return mine_js_1.mineTxPoW; } });
38
38
  Object.defineProperty(exports, "mineTxPoWInProcess", { enumerable: true, get: function () { return mine_js_1.mineTxPoWInProcess; } });
39
+ Object.defineProperty(exports, "mineHeaderTail", { enumerable: true, get: function () { return mine_js_1.mineHeaderTail; } });
40
+ Object.defineProperty(exports, "buildHeaderTail", { enumerable: true, get: function () { return mine_js_1.buildHeaderTail; } });
39
41
  Object.defineProperty(exports, "isLessThan", { enumerable: true, get: function () { return mine_js_1.isLessThan; } });
40
42
  Object.defineProperty(exports, "setBrowserWorkerUrl", { enumerable: true, get: function () { return mine_js_1.setBrowserWorkerUrl; } });
41
43
  var mine_wasm_js_1 = require("./mine-wasm.js");
@@ -51,3 +53,33 @@ Object.defineProperty(exports, "verifyTxPoWParts", { enumerable: true, get: func
51
53
  var calibrate_js_1 = require("./calibrate.js");
52
54
  Object.defineProperty(exports, "calibrateHashRate", { enumerable: true, get: function () { return calibrate_js_1.calibrateHashRate; } });
53
55
  Object.defineProperty(exports, "estimateMiningCost", { enumerable: true, get: function () { return calibrate_js_1.estimateMiningCost; } });
56
+ // ── Machine Work Admission ──────────────────────────────────────────────────
57
+ // Receiver-controlled computational admission bound to a real Minima block
58
+ // candidate. The same nonce search that admits a machine action also searches
59
+ // for Minima blocks (Super-0 … Super-31).
60
+ var index_js_1 = require("./admission/index.js");
61
+ Object.defineProperty(exports, "createWorkChallenge", { enumerable: true, get: function () { return index_js_1.createWorkChallenge; } });
62
+ Object.defineProperty(exports, "validateWorkChallenge", { enumerable: true, get: function () { return index_js_1.validateWorkChallenge; } });
63
+ Object.defineProperty(exports, "challengeFingerprint", { enumerable: true, get: function () { return index_js_1.challengeFingerprint; } });
64
+ Object.defineProperty(exports, "canonicalChallenge", { enumerable: true, get: function () { return index_js_1.canonicalChallenge; } });
65
+ Object.defineProperty(exports, "computeActionCommitment", { enumerable: true, get: function () { return index_js_1.computeActionCommitment; } });
66
+ Object.defineProperty(exports, "canonicalAction", { enumerable: true, get: function () { return index_js_1.canonicalAction; } });
67
+ Object.defineProperty(exports, "mineWorkAdmission", { enumerable: true, get: function () { return index_js_1.mineWorkAdmission; } });
68
+ Object.defineProperty(exports, "verifyWorkAdmission", { enumerable: true, get: function () { return index_js_1.verifyWorkAdmission; } });
69
+ Object.defineProperty(exports, "buildBlockHeaderTail", { enumerable: true, get: function () { return index_js_1.buildBlockHeaderTail; } });
70
+ Object.defineProperty(exports, "serializeSuperParents", { enumerable: true, get: function () { return index_js_1.serializeSuperParents; } });
71
+ Object.defineProperty(exports, "isBlockWinner", { enumerable: true, get: function () { return index_js_1.isBlockWinner; } });
72
+ Object.defineProperty(exports, "computeSuperLevel", { enumerable: true, get: function () { return index_js_1.computeSuperLevel; } });
73
+ Object.defineProperty(exports, "templateFreshness", { enumerable: true, get: function () { return index_js_1.templateFreshness; } });
74
+ Object.defineProperty(exports, "computeBlockCandidateId", { enumerable: true, get: function () { return index_js_1.computeBlockCandidateId; } });
75
+ Object.defineProperty(exports, "buildEmptyBlockBody", { enumerable: true, get: function () { return index_js_1.buildEmptyBlockBody; } });
76
+ Object.defineProperty(exports, "buildEmptyTransactionBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyTransactionBytes; } });
77
+ Object.defineProperty(exports, "buildEmptyWitnessBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyWitnessBytes; } });
78
+ Object.defineProperty(exports, "buildEmptyBurnTxBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyBurnTxBytes; } });
79
+ Object.defineProperty(exports, "buildEmptyBurnWitnessBytes", { enumerable: true, get: function () { return index_js_1.buildEmptyBurnWitnessBytes; } });
80
+ Object.defineProperty(exports, "assembleTxPoWEnvelope", { enumerable: true, get: function () { return index_js_1.assembleTxPoWEnvelope; } });
81
+ Object.defineProperty(exports, "reconstructTxPoWEnvelope", { enumerable: true, get: function () { return index_js_1.reconstructTxPoWEnvelope; } });
82
+ Object.defineProperty(exports, "MACHINE_WORK_ADMISSION_VERSION", { enumerable: true, get: function () { return index_js_1.MACHINE_WORK_ADMISSION_VERSION; } });
83
+ Object.defineProperty(exports, "MACHINE_WORK_DOMAIN", { enumerable: true, get: function () { return index_js_1.MACHINE_WORK_DOMAIN; } });
84
+ Object.defineProperty(exports, "DEFAULT_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return index_js_1.DEFAULT_CHALLENGE_TTL_MS; } });
85
+ Object.defineProperty(exports, "MAX_CHALLENGE_TTL_MS", { enumerable: true, get: function () { return index_js_1.MAX_CHALLENGE_TTL_MS; } });
package/dist/mine.d.ts CHANGED
@@ -72,6 +72,17 @@ export interface MineOptions {
72
72
  * Used to check: txpowId < mTxnDifficulty.
73
73
  */
74
74
  export declare function isLessThan(a: Uint8Array, b: Uint8Array): boolean;
75
+ /**
76
+ * Build the "header tail" — the part of TxHeader that follows mNonce.
77
+ *
78
+ * This is computed once at the start of a mine and never changes during it.
79
+ *
80
+ * This is the fresh-transaction header shape (blockNumber=0, MAX_HASH block
81
+ * difficulty, zero super-parents, zero MMR, zero customHash). Block-candidate
82
+ * mining (Machine Work Admission) builds its own tail via
83
+ * `admission/template.ts` with real chain state and a customHash commitment.
84
+ */
85
+ export declare function buildHeaderTail(txBodyHash: Uint8Array, timeMilli: bigint): Uint8Array;
75
86
  /**
76
87
  * Configure the URL of the browser.worker.js bundle so that `mineTxPoW()`
77
88
  * can spawn a Web Worker in browser contexts.
@@ -84,6 +95,19 @@ export declare function isLessThan(a: Uint8Array, b: Uint8Array): boolean;
84
95
  * setBrowserWorkerUrl(browser.runtime.getURL('mine-worker.js'));
85
96
  */
86
97
  export declare function setBrowserWorkerUrl(url: string): void;
98
+ /**
99
+ * Core mining loop over a pre-built header tail.
100
+ *
101
+ * Iterates the nonce in the serialized TxHeader (nonce value at offset 2,
102
+ * followed by `headerTail`) until `SHA3-256(header) < target`. Shared by the
103
+ * transaction miner (`mineTxPoWInProcess`) and the Machine Work Admission
104
+ * miner (`admission/mine.ts`), which supplies its own block-candidate tail.
105
+ *
106
+ * @param headerTail Serialized TxHeader bytes AFTER the nonce field.
107
+ * @param target 32-byte difficulty target (big-endian 256-bit).
108
+ * @param options Chunk size, max iterations, abort signal, timeMilli override.
109
+ */
110
+ export declare function mineHeaderTail(headerTail: Uint8Array, target: Uint8Array, options?: MineOptions): Promise<MineResult>;
87
111
  /**
88
112
  * Run the mining loop in the current thread/task.
89
113
  * Called directly by the Node.js worker, and by the browser code path.
package/dist/mine.js CHANGED
@@ -29,7 +29,9 @@
29
29
  */
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
31
  exports.isLessThan = isLessThan;
32
+ exports.buildHeaderTail = buildHeaderTail;
32
33
  exports.setBrowserWorkerUrl = setBrowserWorkerUrl;
34
+ exports.mineHeaderTail = mineHeaderTail;
33
35
  exports.mineTxPoWInProcess = mineTxPoWInProcess;
34
36
  exports.mineTxPoW = mineTxPoW;
35
37
  const core_1 = require("@totemsdk/core");
@@ -70,6 +72,11 @@ function nextNonceBoundary(nonce) {
70
72
  * Build the "header tail" — the part of TxHeader that follows mNonce.
71
73
  *
72
74
  * This is computed once at the start of a mine and never changes during it.
75
+ *
76
+ * This is the fresh-transaction header shape (blockNumber=0, MAX_HASH block
77
+ * difficulty, zero super-parents, zero MMR, zero customHash). Block-candidate
78
+ * mining (Machine Work Admission) builds its own tail via
79
+ * `admission/template.ts` with real chain state and a customHash commitment.
73
80
  */
74
81
  function buildHeaderTail(txBodyHash, timeMilli) {
75
82
  return (0, core_2.concat)((0, core_2.writeMiniData)(constants_js_1.MAIN_NET_CHAIN_ID), (0, core_2.writeMiniNumber)(timeMilli, 0), (0, core_2.writeMiniNumber)(0n, 0), (0, core_2.writeMiniData)(constants_js_1.MAX_HASH), new Uint8Array([constants_js_1.CASCADE_LEVELS]), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeMiniNumber)(0n, 0), (0, magic_js_1.serializeMagic)(), (0, core_2.writeHashToStream)(constants_js_1.ZERO_HASH), (0, core_2.writeHashToStream)(txBodyHash));
@@ -283,19 +290,24 @@ async function mineTxPoWViaNodeWorker(txBodyBytes, txnDifficulty, options) {
283
290
  // In-process mining loop
284
291
  // ─────────────────────────────────────────────────────────────────────────────
285
292
  /**
286
- * Run the mining loop in the current thread/task.
287
- * Called directly by the Node.js worker, and by the browser code path.
293
+ * Core mining loop over a pre-built header tail.
294
+ *
295
+ * Iterates the nonce in the serialized TxHeader (nonce value at offset 2,
296
+ * followed by `headerTail`) until `SHA3-256(header) < target`. Shared by the
297
+ * transaction miner (`mineTxPoWInProcess`) and the Machine Work Admission
298
+ * miner (`admission/mine.ts`), which supplies its own block-candidate tail.
299
+ *
300
+ * @param headerTail Serialized TxHeader bytes AFTER the nonce field.
301
+ * @param target 32-byte difficulty target (big-endian 256-bit).
302
+ * @param options Chunk size, max iterations, abort signal, timeMilli override.
288
303
  */
289
- async function mineTxPoWInProcess(txBodyBytes, txnDifficulty, options) {
304
+ async function mineHeaderTail(headerTail, target, options) {
290
305
  const chunkSize = BigInt(options?.chunkSize ?? 10000);
291
306
  const maxIterations = options?.maxIterations !== undefined
292
307
  ? BigInt(options.maxIterations)
293
308
  : undefined;
294
309
  const signal = options?.signal;
295
- const timeMilli = options?.timeMilli ?? BigInt(Date.now());
296
310
  const forceJs = options?.forceJs ?? false;
297
- const bodyHash = (0, core_1.sha3_256)(txBodyBytes);
298
- const headerTail = buildHeaderTail(bodyHash, timeMilli);
299
311
  const useWasm = !forceJs && (await (0, mine_wasm_js_1.isWasmAvailable)());
300
312
  const startTime = Date.now();
301
313
  let totalIterations = 0n;
@@ -319,10 +331,10 @@ async function mineTxPoWInProcess(txBodyBytes, txnDifficulty, options) {
319
331
  let found;
320
332
  if (useWasm) {
321
333
  found = await (0, mine_wasm_js_1.mineChunkWasm)(headerBuf.slice(), // WASM gets its own copy to avoid aliasing
322
- 2, nonceValueLen, txnDifficulty, nonce, chunkEnd - nonce);
334
+ 2, nonceValueLen, target, nonce, chunkEnd - nonce);
323
335
  }
324
336
  else {
325
- found = mineChunkJs(headerBuf, nonceValueLen, txnDifficulty, nonce, chunkEnd);
337
+ found = mineChunkJs(headerBuf, nonceValueLen, target, nonce, chunkEnd);
326
338
  }
327
339
  // ── End mutually exclusive section ───────────────────────────────────────
328
340
  if (found !== null) {
@@ -349,6 +361,16 @@ async function mineTxPoWInProcess(txBodyBytes, txnDifficulty, options) {
349
361
  });
350
362
  }
351
363
  }
364
+ /**
365
+ * Run the mining loop in the current thread/task.
366
+ * Called directly by the Node.js worker, and by the browser code path.
367
+ */
368
+ async function mineTxPoWInProcess(txBodyBytes, txnDifficulty, options) {
369
+ const timeMilli = options?.timeMilli ?? BigInt(Date.now());
370
+ const bodyHash = (0, core_1.sha3_256)(txBodyBytes);
371
+ const headerTail = buildHeaderTail(bodyHash, timeMilli);
372
+ return mineHeaderTail(headerTail, txnDifficulty, options);
373
+ }
352
374
  // ─────────────────────────────────────────────────────────────────────────────
353
375
  // Public API
354
376
  // ─────────────────────────────────────────────────────────────────────────────