@totemsdk/txpow 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/NOTICE +60 -0
- package/dist/admission/challenge.js +14 -21
- package/dist/admission/commitment.js +9 -13
- package/dist/admission/index.js +6 -34
- package/dist/admission/mine.js +14 -17
- package/dist/admission/template.js +32 -47
- package/dist/admission/types.js +2 -5
- package/dist/admission/verify.js +12 -15
- package/dist/calibrate.js +4 -8
- package/dist/cjs/admission/challenge.d.ts +66 -0
- package/dist/cjs/admission/challenge.js +155 -0
- package/dist/cjs/admission/commitment.d.ts +34 -0
- package/dist/cjs/admission/commitment.js +73 -0
- package/dist/cjs/admission/index.d.ts +14 -0
- package/dist/cjs/admission/index.js +42 -0
- package/dist/cjs/admission/mine.d.ts +60 -0
- package/dist/cjs/admission/mine.js +146 -0
- package/dist/cjs/admission/template.d.ts +138 -0
- package/dist/cjs/admission/template.js +247 -0
- package/dist/cjs/admission/types.d.ts +224 -0
- package/dist/cjs/admission/types.js +24 -0
- package/dist/cjs/admission/verify.d.ts +50 -0
- package/dist/cjs/admission/verify.js +156 -0
- package/dist/cjs/calibrate.d.ts +35 -0
- package/dist/cjs/calibrate.js +68 -0
- package/dist/cjs/constants.d.ts +27 -0
- package/dist/cjs/constants.js +47 -0
- package/dist/cjs/difficulty.d.ts +44 -0
- package/dist/cjs/difficulty.js +111 -0
- package/dist/cjs/index.d.ts +28 -0
- package/dist/cjs/index.js +88 -0
- package/dist/cjs/magic.d.ts +19 -0
- package/dist/cjs/magic.js +35 -0
- package/dist/cjs/mine-wasm.d.ts +69 -0
- package/dist/cjs/mine-wasm.js +214 -0
- package/dist/cjs/mine.d.ts +131 -0
- package/dist/cjs/mine.js +407 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/serialization.d.ts +106 -0
- package/dist/cjs/serialization.js +160 -0
- package/dist/cjs/verify.d.ts +76 -0
- package/dist/cjs/verify.js +232 -0
- package/dist/cjs/wasm/miner.wasm +0 -0
- package/dist/cjs/workers/browser.worker.d.ts +26 -0
- package/dist/cjs/workers/browser.worker.js +63 -0
- package/dist/cjs/workers/node.worker.d.ts +33 -0
- package/dist/cjs/workers/node.worker.js +47 -0
- package/dist/constants.js +5 -8
- package/dist/difficulty.d.ts +24 -0
- package/dist/difficulty.js +54 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -62
- package/dist/magic.js +12 -15
- package/dist/mine-wasm.js +8 -15
- package/dist/mine.js +23 -31
- package/dist/serialization.js +28 -34
- package/dist/verify.js +15 -20
- package/dist/workers/browser.worker.js +4 -6
- package/dist/workers/node.worker.js +2 -3
- package/package.json +34 -15
|
@@ -0,0 +1,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,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]);
|