openttt 0.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 (46) hide show
  1. package/README.md +391 -0
  2. package/dist/adaptive_switch.d.ts +44 -0
  3. package/dist/adaptive_switch.js +108 -0
  4. package/dist/auto_mint.d.ts +45 -0
  5. package/dist/auto_mint.js +244 -0
  6. package/dist/dynamic_fee.d.ts +64 -0
  7. package/dist/dynamic_fee.js +203 -0
  8. package/dist/errors.d.ts +45 -0
  9. package/dist/errors.js +74 -0
  10. package/dist/evm_connector.d.ts +88 -0
  11. package/dist/evm_connector.js +297 -0
  12. package/dist/golay.d.ts +6 -0
  13. package/dist/golay.js +166 -0
  14. package/dist/grg_forward.d.ts +6 -0
  15. package/dist/grg_forward.js +59 -0
  16. package/dist/grg_inverse.d.ts +7 -0
  17. package/dist/grg_inverse.js +97 -0
  18. package/dist/grg_pipeline.d.ts +13 -0
  19. package/dist/grg_pipeline.js +64 -0
  20. package/dist/index.d.ts +21 -0
  21. package/dist/index.js +38 -0
  22. package/dist/logger.d.ts +18 -0
  23. package/dist/logger.js +51 -0
  24. package/dist/networks.d.ts +13 -0
  25. package/dist/networks.js +23 -0
  26. package/dist/pool_registry.d.ts +58 -0
  27. package/dist/pool_registry.js +129 -0
  28. package/dist/protocol_fee.d.ts +56 -0
  29. package/dist/protocol_fee.js +176 -0
  30. package/dist/reed_solomon.d.ts +12 -0
  31. package/dist/reed_solomon.js +179 -0
  32. package/dist/signer.d.ts +76 -0
  33. package/dist/signer.js +329 -0
  34. package/dist/time_synthesis.d.ts +49 -0
  35. package/dist/time_synthesis.js +372 -0
  36. package/dist/ttt_builder.d.ts +32 -0
  37. package/dist/ttt_builder.js +84 -0
  38. package/dist/ttt_client.d.ts +118 -0
  39. package/dist/ttt_client.js +352 -0
  40. package/dist/types.d.ts +141 -0
  41. package/dist/types.js +9 -0
  42. package/dist/v4_hook.d.ts +43 -0
  43. package/dist/v4_hook.js +115 -0
  44. package/dist/x402_enforcer.d.ts +29 -0
  45. package/dist/x402_enforcer.js +67 -0
  46. package/package.json +51 -0
@@ -0,0 +1,88 @@
1
+ import { JsonRpcProvider, Signer, TransactionReceipt } from "ethers";
2
+ import { TTTRecord } from "./adaptive_switch";
3
+ export interface VerificationResult {
4
+ valid: boolean;
5
+ blockNumber: number;
6
+ timestamp: number;
7
+ txCount: number;
8
+ latency: number;
9
+ }
10
+ export declare class EVMConnector {
11
+ private provider;
12
+ private signer;
13
+ private tttContract;
14
+ private protocolFeeContract;
15
+ private eventListeners;
16
+ private static readonly GAS_TIMEOUT_MS;
17
+ constructor();
18
+ /**
19
+ * P1-7: Race estimateGas against timeout to prevent DoS
20
+ */
21
+ private withTimeout;
22
+ /**
23
+ * Connect to an EVM chain using either a private key or a pre-configured signer.
24
+ */
25
+ connect(rpcUrl: string, signerOrKey: string | Signer): Promise<void>;
26
+ /**
27
+ * Attach the TTT Token contract.
28
+ */
29
+ attachContract(address: string, abi: any[]): void;
30
+ /**
31
+ * Attach the ProtocolFee contract.
32
+ */
33
+ attachProtocolFeeContract(address: string, abi: any[]): void;
34
+ /**
35
+ * Generic TTT Record Submission (Burn)
36
+ */
37
+ submitTTTRecord(record: TTTRecord, amount: bigint, tier: number): Promise<TransactionReceipt>;
38
+ /**
39
+ * Mint TTT (Owner only)
40
+ */
41
+ mintTTT(to: string, amount: bigint, grgHash: string, potHash?: string): Promise<TransactionReceipt>;
42
+ /**
43
+ * Burn TTT (Simple wrapper)
44
+ */
45
+ burnTTT(amount: bigint, grgHash: string, tierLevel: number): Promise<{
46
+ hash: string;
47
+ }>;
48
+ /**
49
+ * Get TTT Balance (ERC-1155)
50
+ */
51
+ getTTTBalance(user: string, tokenId: bigint): Promise<bigint>;
52
+ /**
53
+ * Swap tokens on a DEX (Uniswap V4 Simulation)
54
+ */
55
+ swap(routerAddress: string, tokenIn: string, tokenOut: string, amountIn: bigint, minAmountOut: bigint): Promise<TransactionReceipt>;
56
+ /**
57
+ * Subscribe to TTT and Fee events.
58
+ */
59
+ subscribeToEvents(callbacks: {
60
+ onMinted?: (to: string, tokenId: bigint, amount: bigint) => void;
61
+ onBurned?: (from: string, tokenId: bigint, amount: bigint, tier: bigint) => void;
62
+ onFeeCollected?: (payer: string, amount: bigint, nonce: bigint) => void;
63
+ }): Promise<void>;
64
+ /**
65
+ * P2-6: Unsubscribe all event listeners to prevent memory leaks.
66
+ */
67
+ unsubscribeAll(): void;
68
+ /**
69
+ * Verify Block Data
70
+ */
71
+ verifyBlock(blockNum: number): Promise<VerificationResult>;
72
+ /**
73
+ * Get pending transactions for the current provider.
74
+ */
75
+ getPendingTransactions(): Promise<string[]>;
76
+ /**
77
+ * Get the provider instance.
78
+ */
79
+ getProvider(): JsonRpcProvider;
80
+ /**
81
+ * Get the signer instance.
82
+ */
83
+ getSigner(): Signer;
84
+ /**
85
+ * Extract human-readable revert reason from ethers error.
86
+ */
87
+ private extractRevertReason;
88
+ }
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ // sdk/src/evm_connector.ts — Production EVM Chain Connector
3
+ // Supports EIP-1559, Gas Estimation, and TTT Operations.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.EVMConnector = void 0;
6
+ const ethers_1 = require("ethers");
7
+ const logger_1 = require("./logger");
8
+ const errors_1 = require("./errors");
9
+ class EVMConnector {
10
+ provider = null;
11
+ signer = null;
12
+ tttContract = null;
13
+ protocolFeeContract = null;
14
+ eventListeners = []; // P2-6: Track listeners for cleanup
15
+ // P1-7: Timeout wrapper for gas estimation
16
+ static GAS_TIMEOUT_MS = 5000;
17
+ constructor() { }
18
+ /**
19
+ * P1-7: Race estimateGas against timeout to prevent DoS
20
+ */
21
+ async withTimeout(promise, ms = EVMConnector.GAS_TIMEOUT_MS) {
22
+ return Promise.race([
23
+ promise,
24
+ new Promise((_, reject) => setTimeout(() => reject(new errors_1.TTTNetworkError(`[EVM] Operation timed out`, `RPC did not respond within ${ms}ms`, `Check your RPC provider status or increase timeout.`)), ms))
25
+ ]);
26
+ }
27
+ /**
28
+ * Connect to an EVM chain using either a private key or a pre-configured signer.
29
+ */
30
+ async connect(rpcUrl, signerOrKey) {
31
+ if (!rpcUrl || typeof rpcUrl !== "string")
32
+ throw new errors_1.TTTNetworkError("[EVM] Invalid RPC URL", "The provided RPC URL is empty or not a string", "Pass a valid RPC URL (e.g., https://mainnet.base.org)");
33
+ try {
34
+ this.provider = new ethers_1.JsonRpcProvider(rpcUrl);
35
+ if (typeof signerOrKey === "string") {
36
+ if (!signerOrKey.startsWith("0x") || signerOrKey.length !== 66) {
37
+ throw new errors_1.TTTContractError("[EVM] Invalid Private Key format", "Private key must be 0x + 64 hex characters", "Provide a valid 32-byte hex private key.");
38
+ }
39
+ this.signer = new ethers_1.ethers.Wallet(signerOrKey, this.provider);
40
+ }
41
+ else {
42
+ // Signer might already be connected to a provider, but we ensure it's linked to ours
43
+ this.signer = signerOrKey.connect ? signerOrKey.connect(this.provider) : signerOrKey;
44
+ }
45
+ const network = await this.provider.getNetwork();
46
+ logger_1.logger.info(`[EVM] Connected to Chain ID: ${network.chainId}`);
47
+ }
48
+ catch (error) {
49
+ if (error instanceof errors_1.TTTContractError || error instanceof errors_1.TTTNetworkError)
50
+ throw error;
51
+ throw new errors_1.TTTNetworkError(`[EVM] Connection failed`, error instanceof Error ? error.message : String(error), `Verify your RPC URL and network connectivity.`);
52
+ }
53
+ }
54
+ /**
55
+ * Attach the TTT Token contract.
56
+ */
57
+ attachContract(address, abi) {
58
+ if (!this.signer)
59
+ throw new errors_1.TTTContractError("Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
60
+ if (!address || !ethers_1.ethers.isAddress(address))
61
+ throw new errors_1.TTTContractError(`[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
62
+ this.tttContract = new ethers_1.Contract(address, abi, this.signer);
63
+ }
64
+ /**
65
+ * Attach the ProtocolFee contract.
66
+ */
67
+ attachProtocolFeeContract(address, abi) {
68
+ if (!this.signer)
69
+ throw new errors_1.TTTContractError("Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
70
+ if (!address || !ethers_1.ethers.isAddress(address))
71
+ throw new errors_1.TTTContractError(`[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
72
+ this.protocolFeeContract = new ethers_1.Contract(address, abi, this.signer);
73
+ }
74
+ /**
75
+ * Generic TTT Record Submission (Burn)
76
+ */
77
+ async submitTTTRecord(record, amount, tier) {
78
+ if (!this.tttContract)
79
+ throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() with valid TTT address before burning.");
80
+ const grgHash = ethers_1.ethers.keccak256(ethers_1.ethers.concat(record.grgPayload));
81
+ try {
82
+ // P1-7: Gas estimation with timeout
83
+ const gasLimit = await this.withTimeout(this.tttContract.burn.estimateGas(amount, grgHash, tier));
84
+ const tx = await this.tttContract.burn(amount, grgHash, tier, {
85
+ gasLimit: (gasLimit * 120n) / 100n
86
+ });
87
+ logger_1.logger.info(`[EVM] TTT Record TX Sent: ${tx.hash}`);
88
+ const receipt = await tx.wait();
89
+ // P2-5: Null check for dropped transactions
90
+ if (!receipt)
91
+ throw new errors_1.TTTNetworkError(`[EVM] Transaction failed`, `Transaction was dropped from mempool or null receipt`, `Check block explorer for tx status.`);
92
+ return receipt;
93
+ }
94
+ catch (error) {
95
+ if (error instanceof errors_1.TTTNetworkError || error instanceof errors_1.TTTContractError)
96
+ throw error;
97
+ const reason = this.extractRevertReason(error);
98
+ throw new errors_1.TTTContractError(`[EVM] Burn failed`, reason, `Verify your TTT balance and tier parameters.`);
99
+ }
100
+ }
101
+ /**
102
+ * Mint TTT (Owner only)
103
+ */
104
+ async mintTTT(to, amount, grgHash, potHash) {
105
+ if (!this.tttContract)
106
+ throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before minting.");
107
+ if (!to || !ethers_1.ethers.isAddress(to))
108
+ throw new errors_1.TTTContractError(`[EVM] Invalid recipient address`, `Address '${to}' is not a valid EVM address`, `Provide a valid destination address.`);
109
+ try {
110
+ if (potHash) {
111
+ logger_1.logger.info(`[EVM] Recording PoT fingerprint: ${potHash}`);
112
+ }
113
+ const tx = await this.tttContract.mint(to, amount, grgHash);
114
+ const receipt = await tx.wait();
115
+ if (!receipt)
116
+ throw new errors_1.TTTNetworkError(`[EVM] Mint TX dropped`, `Transaction was dropped from mempool`, `Check operator account for nonce collisions.`);
117
+ return receipt;
118
+ }
119
+ catch (error) {
120
+ const reason = this.extractRevertReason(error);
121
+ throw new errors_1.TTTContractError(`[EVM] Mint failed`, reason, `Ensure operator has minter role and sufficient gas.`);
122
+ }
123
+ }
124
+ /**
125
+ * Burn TTT (Simple wrapper)
126
+ */
127
+ async burnTTT(amount, grgHash, tierLevel) {
128
+ if (!this.tttContract)
129
+ throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before burning.");
130
+ try {
131
+ const tx = await this.tttContract.burn(amount, grgHash, tierLevel);
132
+ const receipt = await tx.wait();
133
+ if (!receipt)
134
+ throw new errors_1.TTTNetworkError(`[EVM] Burn TX dropped`, `Transaction was dropped from mempool`, `Verify account balance.`);
135
+ return { hash: receipt.hash };
136
+ }
137
+ catch (error) {
138
+ const reason = this.extractRevertReason(error);
139
+ throw new errors_1.TTTContractError(`[EVM] Burn failed`, reason, `Check TTT balance.`);
140
+ }
141
+ }
142
+ /**
143
+ * Get TTT Balance (ERC-1155)
144
+ */
145
+ async getTTTBalance(user, tokenId) {
146
+ if (!this.tttContract)
147
+ throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before querying balance.");
148
+ try {
149
+ return await this.tttContract.balanceOf(user, tokenId);
150
+ }
151
+ catch (error) {
152
+ const reason = this.extractRevertReason(error);
153
+ throw new errors_1.TTTContractError(`[EVM] Balance query failed`, reason, `Check RPC connection and contract address.`);
154
+ }
155
+ }
156
+ /**
157
+ * Swap tokens on a DEX (Uniswap V4 Simulation)
158
+ */
159
+ async swap(routerAddress, tokenIn, tokenOut, amountIn, minAmountOut) {
160
+ if (!this.signer)
161
+ throw new errors_1.TTTContractError("Not connected to signer", "Signer is null", "Initialize connection.");
162
+ if (!routerAddress || !ethers_1.ethers.isAddress(routerAddress))
163
+ throw new errors_1.TTTContractError(`[EVM] Invalid router address`, `Address '${routerAddress}' is invalid`, `Provide valid V4 SwapRouter address.`);
164
+ logger_1.logger.info(`[EVM] Swapping ${amountIn} of ${tokenIn} for ${tokenOut} via ${routerAddress}`);
165
+ // Realistic Uniswap V4-like SwapRouter ABI for simulation/integration
166
+ const swapRouterAbi = [
167
+ "function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMinimum) external returns (uint256)"
168
+ ];
169
+ const routerContract = new ethers_1.ethers.Contract(routerAddress, swapRouterAbi, this.signer);
170
+ try {
171
+ // P1-7: Gas estimation with timeout
172
+ const gasLimit = await this.withTimeout(routerContract.swap.estimateGas(tokenIn, tokenOut, amountIn, minAmountOut));
173
+ const tx = await routerContract.swap(tokenIn, tokenOut, amountIn, minAmountOut, {
174
+ gasLimit: (gasLimit * 120n) / 100n
175
+ });
176
+ logger_1.logger.info(`[EVM] Swap TX Sent: ${tx.hash}`);
177
+ const receipt = await tx.wait();
178
+ if (!receipt)
179
+ throw new errors_1.TTTNetworkError(`[EVM] Swap TX dropped`, `Transaction dropped`, `Check gas price.`);
180
+ return receipt;
181
+ }
182
+ catch (error) {
183
+ if (error instanceof errors_1.TTTNetworkError || error instanceof errors_1.TTTContractError)
184
+ throw error;
185
+ const reason = this.extractRevertReason(error);
186
+ throw new errors_1.TTTContractError(`[EVM] Swap failed`, reason, `Verify slippage and token balances.`);
187
+ }
188
+ }
189
+ /**
190
+ * Subscribe to TTT and Fee events.
191
+ */
192
+ async subscribeToEvents(callbacks) {
193
+ // R2-P1-1: Auto-cleanup previous listeners before re-subscribing (idempotency)
194
+ if (this.eventListeners.length > 0) {
195
+ this.unsubscribeAll();
196
+ }
197
+ if (this.tttContract) {
198
+ if (callbacks.onMinted) {
199
+ const handler = (to, tokenId, amount) => {
200
+ callbacks.onMinted(to, tokenId, amount);
201
+ };
202
+ this.tttContract.on("TTTMinted", handler);
203
+ // R6-P1-2: Store direct handler reference for reliable .off() cleanup
204
+ const contract = this.tttContract;
205
+ this.eventListeners.push(() => contract.off("TTTMinted", handler));
206
+ }
207
+ if (callbacks.onBurned) {
208
+ const handler = (from, tokenId, amount, tier) => {
209
+ callbacks.onBurned(from, tokenId, amount, tier);
210
+ };
211
+ this.tttContract.on("TTTBurned", handler);
212
+ const contract = this.tttContract;
213
+ this.eventListeners.push(() => contract.off("TTTBurned", handler));
214
+ }
215
+ }
216
+ if (this.protocolFeeContract && callbacks.onFeeCollected) {
217
+ const handler = (payer, amount, nonce) => {
218
+ callbacks.onFeeCollected(payer, amount, nonce);
219
+ };
220
+ this.protocolFeeContract.on("FeeCollected", handler);
221
+ const contract = this.protocolFeeContract;
222
+ this.eventListeners.push(() => contract.off("FeeCollected", handler));
223
+ }
224
+ logger_1.logger.info("[EVM] Subscribed to TTT and Fee events");
225
+ }
226
+ /**
227
+ * P2-6: Unsubscribe all event listeners to prevent memory leaks.
228
+ */
229
+ unsubscribeAll() {
230
+ for (const unsub of this.eventListeners) {
231
+ try {
232
+ unsub();
233
+ }
234
+ catch { /* already removed */ }
235
+ }
236
+ this.eventListeners = [];
237
+ logger_1.logger.info(`[EVM] All event listeners unsubscribed`);
238
+ }
239
+ /**
240
+ * Verify Block Data
241
+ */
242
+ async verifyBlock(blockNum) {
243
+ if (!this.provider)
244
+ throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
245
+ const block = await this.provider.getBlock(blockNum);
246
+ if (!block)
247
+ throw new errors_1.TTTNetworkError(`Block not found`, `RPC returned null for block ${blockNum}`, `Verify if block number exists on chain.`);
248
+ return {
249
+ valid: true,
250
+ blockNumber: blockNum,
251
+ timestamp: block.timestamp,
252
+ txCount: block.transactions.length,
253
+ latency: Math.floor(Date.now() / 1000) - block.timestamp
254
+ };
255
+ }
256
+ /**
257
+ * Get pending transactions for the current provider.
258
+ */
259
+ async getPendingTransactions() {
260
+ if (!this.provider)
261
+ throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
262
+ const block = await this.provider.send("eth_getBlockByNumber", ["pending", false]);
263
+ return block ? block.transactions : [];
264
+ }
265
+ /**
266
+ * Get the provider instance.
267
+ */
268
+ getProvider() {
269
+ if (!this.provider)
270
+ throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
271
+ return this.provider;
272
+ }
273
+ /**
274
+ * Get the signer instance.
275
+ */
276
+ getSigner() {
277
+ if (!this.signer)
278
+ throw new errors_1.TTTContractError("Signer not connected", "Signer is null", "Call connect() first.");
279
+ return this.signer;
280
+ }
281
+ /**
282
+ * Extract human-readable revert reason from ethers error.
283
+ */
284
+ extractRevertReason(error) {
285
+ if (error && typeof error === "object") {
286
+ const e = error;
287
+ if (typeof e.reason === "string")
288
+ return e.reason;
289
+ if (e.data && typeof e.data === "object" && typeof e.data.message === "string")
290
+ return e.data.message;
291
+ if (typeof e.message === "string")
292
+ return e.message;
293
+ }
294
+ return String(error ?? "Unknown EVM error");
295
+ }
296
+ }
297
+ exports.EVMConnector = EVMConnector;
@@ -0,0 +1,6 @@
1
+ export declare function golayEncode(data: Uint8Array): Uint8Array;
2
+ export declare function golayDecode(encoded: Uint8Array): {
3
+ data: Uint8Array;
4
+ corrected: number;
5
+ uncorrectable: boolean;
6
+ };
package/dist/golay.js ADDED
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ // sdk/src/golay.ts
3
+ // 🔱 Golay(24,12) Extended Binary Golay Code (RE-SURGERY)
4
+ // Corrects up to 3 bit errors, detects 4 bit errors.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.golayEncode = golayEncode;
7
+ exports.golayDecode = golayDecode;
8
+ // Standard Golay Parity Matrix P (12x12) - Systematic Form [I12 | P]
9
+ // Every row has weight 7.
10
+ // Verified: P*P^T = I (mod 2), all rows weight 7.
11
+ // Derived from g(x) = x^11+x^10+x^6+x^5+x^4+x^2+1 with parity extension.
12
+ const P = [
13
+ 0xC75, 0x49F, 0xD4B, 0x6E3, 0x9B3, 0xB66,
14
+ 0xECC, 0x1ED, 0x3DA, 0x7B4, 0xB1D, 0xE3A
15
+ ];
16
+ // R7-P0-1: P^T (transpose of P) — required for second syndrome computation.
17
+ // P is NOT symmetric, so s2 = s*P^T must use this separate matrix.
18
+ // Without this, decodeWord fails ~24% of weight-2 and ~50% of weight-3 error patterns.
19
+ const PT = [
20
+ 0xAE3, 0xF25, 0x16F, 0x2DE, 0x5BC, 0xB78,
21
+ 0x9D5, 0xC8F, 0x63B, 0xC76, 0x7C9, 0xF92
22
+ ];
23
+ const UNIT_VECTORS = [
24
+ 0x800, 0x400, 0x200, 0x100, 0x080, 0x040,
25
+ 0x020, 0x010, 0x008, 0x004, 0x002, 0x001
26
+ ];
27
+ /**
28
+ * Calculates weight (number of set bits).
29
+ */
30
+ function weight(n) {
31
+ let count = 0;
32
+ let temp = n & 0xFFF;
33
+ while (temp > 0) {
34
+ temp &= (temp - 1);
35
+ count++;
36
+ }
37
+ return count;
38
+ }
39
+ /**
40
+ * Multiplies a 12-bit vector by the parity matrix P.
41
+ */
42
+ function multiplyP(v) {
43
+ let res = 0;
44
+ for (let i = 0; i < 12; i++) {
45
+ if ((v >> (11 - i)) & 1) {
46
+ res ^= P[i];
47
+ }
48
+ }
49
+ return res & 0xFFF;
50
+ }
51
+ /**
52
+ * R7-P0-1: Multiplies a 12-bit vector by P^T (transpose of P).
53
+ * Required for second syndrome computation in decodeWord steps 3-5.
54
+ */
55
+ function multiplyPT(v) {
56
+ let res = 0;
57
+ for (let i = 0; i < 12; i++) {
58
+ if ((v >> (11 - i)) & 1) {
59
+ res ^= PT[i];
60
+ }
61
+ }
62
+ return res & 0xFFF;
63
+ }
64
+ function encodeWord(msg) {
65
+ const parity = multiplyP(msg & 0xFFF);
66
+ return ((msg & 0xFFF) << 12) | parity;
67
+ }
68
+ /**
69
+ * Full Syndrome Decoding for Golay(24,12)
70
+ */
71
+ function decodeWord(received) {
72
+ let r_m = (received >> 12) & 0xFFF;
73
+ const r_p = received & 0xFFF;
74
+ // Syndrome s = r_m * P + r_p
75
+ const s = multiplyP(r_m) ^ r_p;
76
+ if (s === 0)
77
+ return { msg: r_m, corrected: 0, uncorrectable: false };
78
+ // 1. wt(s) <= 3 -> Error in parity
79
+ if (weight(s) <= 3) {
80
+ return { msg: r_m, corrected: weight(s), uncorrectable: false };
81
+ }
82
+ // 2. wt(s + P_i) <= 2 -> Error in msg bit i (+ possible 1-2 errors in parity)
83
+ for (let i = 0; i < 12; i++) {
84
+ if (weight(s ^ P[i]) <= 2) {
85
+ return { msg: r_m ^ UNIT_VECTORS[i], corrected: weight(s ^ P[i]) + 1, uncorrectable: false };
86
+ }
87
+ }
88
+ // 3. R7-P0-1: Second syndrome s2 = s * P^T (NOT s * P — P is not symmetric!)
89
+ const s2 = multiplyPT(s);
90
+ // 4. wt(s2) <= 3 -> Error in message
91
+ if (weight(s2) <= 3) {
92
+ return { msg: r_m ^ s2, corrected: weight(s2), uncorrectable: false };
93
+ }
94
+ // 5. wt(s2 + PT_i) <= 2 -> Error in parity bit i + message errors
95
+ // R7-P0-1: Must use PT rows (columns of P), not P rows
96
+ for (let i = 0; i < 12; i++) {
97
+ if (weight(s2 ^ PT[i]) <= 2) {
98
+ const error_m = s2 ^ PT[i];
99
+ return { msg: r_m ^ error_m, corrected: weight(s2 ^ PT[i]) + 1, uncorrectable: false };
100
+ }
101
+ }
102
+ return { msg: r_m, corrected: 0, uncorrectable: true };
103
+ }
104
+ function golayEncode(data) {
105
+ // B1-6: Correct output size calculation to avoid buffer overflow
106
+ const out = new Uint8Array(Math.ceil(data.length / 3) * 6);
107
+ let outIdx = 0;
108
+ // Process in 3-byte blocks -> two 12-bit words -> two 24-bit (3-byte) codewords
109
+ for (let i = 0; i < data.length; i += 3) {
110
+ const b1 = data[i];
111
+ const b2 = i + 1 < data.length ? data[i + 1] : 0;
112
+ const b3 = i + 2 < data.length ? data[i + 2] : 0;
113
+ // Word 1 (12 bits)
114
+ const w1 = (b1 << 4) | (b2 >> 4);
115
+ const c1 = encodeWord(w1);
116
+ out[outIdx++] = (c1 >> 16) & 0xFF;
117
+ out[outIdx++] = (c1 >> 8) & 0xFF;
118
+ out[outIdx++] = c1 & 0xFF;
119
+ if (i + 1 < data.length) {
120
+ // Word 2 (12 bits)
121
+ const w2 = ((b2 & 0x0F) << 8) | b3;
122
+ const c2 = encodeWord(w2);
123
+ out[outIdx++] = (c2 >> 16) & 0xFF;
124
+ out[outIdx++] = (c2 >> 8) & 0xFF;
125
+ out[outIdx++] = c2 & 0xFF;
126
+ }
127
+ }
128
+ return out;
129
+ }
130
+ function golayDecode(encoded) {
131
+ // B1-6: Throws if length is not a multiple of 6
132
+ if (encoded.length % 6 !== 0) {
133
+ throw new Error("Invalid Golay encoded data: length must be multiple of 6");
134
+ }
135
+ const outLen = Math.floor(encoded.length / 2);
136
+ const out = new Uint8Array(outLen);
137
+ let outIdx = 0;
138
+ let totalCorrected = 0;
139
+ let anyUncorrectable = false;
140
+ for (let i = 0; i < encoded.length; i += 6) {
141
+ const c1 = (encoded[i] << 16) | (encoded[i + 1] << 8) | encoded[i + 2];
142
+ const res1 = decodeWord(c1);
143
+ totalCorrected += res1.corrected;
144
+ if (res1.uncorrectable)
145
+ anyUncorrectable = true;
146
+ if (outIdx < outLen)
147
+ out[outIdx++] = (res1.msg >> 4) & 0xFF;
148
+ const b2_high = (res1.msg & 0x0F) << 4;
149
+ if (i + 3 < encoded.length) {
150
+ const c2 = (encoded[i + 3] << 16) | (encoded[i + 4] << 8) | encoded[i + 5];
151
+ const res2 = decodeWord(c2);
152
+ totalCorrected += res2.corrected;
153
+ if (res2.uncorrectable)
154
+ anyUncorrectable = true;
155
+ if (outIdx < outLen)
156
+ out[outIdx++] = b2_high | (res2.msg >> 8);
157
+ if (outIdx < outLen)
158
+ out[outIdx++] = res2.msg & 0xFF;
159
+ }
160
+ else {
161
+ if (outIdx < outLen)
162
+ out[outIdx++] = b2_high;
163
+ }
164
+ }
165
+ return { data: out, corrected: totalCorrected, uncorrectable: anyUncorrectable };
166
+ }
@@ -0,0 +1,6 @@
1
+ export declare class GrgForward {
2
+ static golombEncode(data: Uint8Array, m?: number): Uint8Array;
3
+ static redstuffEncode(data: Uint8Array, shards?: number, parity?: number): Uint8Array[];
4
+ static golayEncodeWrapper(data: Uint8Array): Uint8Array;
5
+ static encode(data: Uint8Array): Uint8Array[];
6
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GrgForward = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const golay_1 = require("./golay");
6
+ const reed_solomon_1 = require("./reed_solomon");
7
+ class GrgForward {
8
+ // 1. Golomb-Rice Compression
9
+ static golombEncode(data, m = 16) {
10
+ if (m < 2)
11
+ throw new Error("[GRG] Golomb parameter m must be >= 2");
12
+ const k = Math.log2(m);
13
+ if (!Number.isInteger(k))
14
+ throw new Error("M must be power of 2");
15
+ let bits = "";
16
+ for (const byte of data) {
17
+ const q = Math.floor(byte / m);
18
+ const r = byte % m;
19
+ bits += "1".repeat(q) + "0" + r.toString(2).padStart(k, "0");
20
+ }
21
+ const bytes = [];
22
+ for (let i = 0; i < bits.length; i += 8) {
23
+ bytes.push(parseInt(bits.substring(i, i + 8).padEnd(8, "0"), 2));
24
+ }
25
+ return new Uint8Array(bytes);
26
+ }
27
+ // 2. RedStuff Erasure Coding (Reed-Solomon GF(2^8))
28
+ static redstuffEncode(data, shards = 4, parity = 2) {
29
+ return reed_solomon_1.ReedSolomon.encode(data, shards, parity);
30
+ }
31
+ // 3. Golay(24,12) Error Correction Encoding
32
+ static golayEncodeWrapper(data) {
33
+ const encoded = (0, golay_1.golayEncode)(data);
34
+ // 🔱 Integrity: Append 8-byte SHA-256 hash of the encoded shard (B1-5: 4 -> 8 bytes)
35
+ const hash = (0, crypto_1.createHash)("sha256").update(Buffer.from(encoded)).digest();
36
+ const checksum = hash.subarray(0, 8);
37
+ const final = new Uint8Array(encoded.length + 8);
38
+ final.set(encoded);
39
+ final.set(checksum, encoded.length);
40
+ return final;
41
+ }
42
+ static encode(data) {
43
+ // R3-P0-3: Reject empty input — roundtrip breaks ([] → [0])
44
+ if (data.length === 0) {
45
+ throw new Error("[GRG] Cannot encode empty input — roundtrip identity violation");
46
+ }
47
+ const compressed = this.golombEncode(data);
48
+ // Prepend original length (4 bytes, big-endian) for exact roundtrip
49
+ const withLen = new Uint8Array(4 + compressed.length);
50
+ withLen[0] = (data.length >> 24) & 0xFF;
51
+ withLen[1] = (data.length >> 16) & 0xFF;
52
+ withLen[2] = (data.length >> 8) & 0xFF;
53
+ withLen[3] = data.length & 0xFF;
54
+ withLen.set(compressed, 4);
55
+ const shards = this.redstuffEncode(withLen);
56
+ return shards.map(s => this.golayEncodeWrapper(s));
57
+ }
58
+ }
59
+ exports.GrgForward = GrgForward;
@@ -0,0 +1,7 @@
1
+ export declare class GrgInverse {
2
+ static golayDecodeWrapper(data: Uint8Array): Uint8Array;
3
+ static redstuffDecode(shards: (Uint8Array | null)[], dataShardCount?: number, parityShardCount?: number): Uint8Array;
4
+ private static readonly MAX_GOLOMB_Q;
5
+ static golombDecode(data: Uint8Array, m?: number): Uint8Array;
6
+ static verify(data: Uint8Array, originalShards: Uint8Array[]): boolean;
7
+ }