openttt 0.2.7 → 0.2.8
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/package.json +45 -2
- package/dist/adaptive_switch.d.ts +0 -59
- package/dist/adaptive_switch.js +0 -145
- package/dist/auto_mint.d.ts +0 -61
- package/dist/auto_mint.js +0 -330
- package/dist/golay.d.ts +0 -7
- package/dist/golay.js +0 -139
- package/dist/grg_forward.d.ts +0 -7
- package/dist/grg_forward.js +0 -68
- package/dist/grg_inverse.d.ts +0 -7
- package/dist/grg_inverse.js +0 -93
- package/dist/grg_pipeline.d.ts +0 -5
- package/dist/grg_pipeline.js +0 -55
- package/dist/reed_solomon.d.ts +0 -13
- package/dist/reed_solomon.js +0 -170
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openttt",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "OpenTTT — TLS-grade transaction ordering for DeFi. Time + Logic + Sync.",
|
|
5
5
|
"license": "BSL-1.1",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,50 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
|
-
"dist",
|
|
33
|
+
"dist/ct_log.js",
|
|
34
|
+
"dist/ct_log.d.ts",
|
|
35
|
+
"dist/dynamic_fee.js",
|
|
36
|
+
"dist/dynamic_fee.d.ts",
|
|
37
|
+
"dist/errors.js",
|
|
38
|
+
"dist/errors.d.ts",
|
|
39
|
+
"dist/evm_connector.js",
|
|
40
|
+
"dist/evm_connector.d.ts",
|
|
41
|
+
"dist/http_client.js",
|
|
42
|
+
"dist/http_client.d.ts",
|
|
43
|
+
"dist/index.js",
|
|
44
|
+
"dist/index.d.ts",
|
|
45
|
+
"dist/integrity_client.js",
|
|
46
|
+
"dist/integrity_client.d.ts",
|
|
47
|
+
"dist/logger.js",
|
|
48
|
+
"dist/logger.d.ts",
|
|
49
|
+
"dist/networks.js",
|
|
50
|
+
"dist/networks.d.ts",
|
|
51
|
+
"dist/osnma_source.js",
|
|
52
|
+
"dist/osnma_source.d.ts",
|
|
53
|
+
"dist/pool_registry.js",
|
|
54
|
+
"dist/pool_registry.d.ts",
|
|
55
|
+
"dist/pot_signer.js",
|
|
56
|
+
"dist/pot_signer.d.ts",
|
|
57
|
+
"dist/protocol_fee.js",
|
|
58
|
+
"dist/protocol_fee.d.ts",
|
|
59
|
+
"dist/revenue_tiers.js",
|
|
60
|
+
"dist/revenue_tiers.d.ts",
|
|
61
|
+
"dist/signer.js",
|
|
62
|
+
"dist/signer.d.ts",
|
|
63
|
+
"dist/time_synthesis.js",
|
|
64
|
+
"dist/time_synthesis.d.ts",
|
|
65
|
+
"dist/trust_store.js",
|
|
66
|
+
"dist/trust_store.d.ts",
|
|
67
|
+
"dist/ttt_builder.js",
|
|
68
|
+
"dist/ttt_builder.d.ts",
|
|
69
|
+
"dist/ttt_client.js",
|
|
70
|
+
"dist/ttt_client.d.ts",
|
|
71
|
+
"dist/types.js",
|
|
72
|
+
"dist/types.d.ts",
|
|
73
|
+
"dist/v4_hook.js",
|
|
74
|
+
"dist/v4_hook.d.ts",
|
|
75
|
+
"dist/x402_enforcer.js",
|
|
76
|
+
"dist/x402_enforcer.d.ts",
|
|
34
77
|
"README.md"
|
|
35
78
|
],
|
|
36
79
|
"scripts": {
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export declare enum AdaptiveMode {
|
|
2
|
-
TURBO = "TURBO",// 50ms — Valid sequence, low latency
|
|
3
|
-
FULL = "FULL"
|
|
4
|
-
}
|
|
5
|
-
export interface TTTRecord {
|
|
6
|
-
time: number;
|
|
7
|
-
txOrder: string[];
|
|
8
|
-
grgPayload: Uint8Array[];
|
|
9
|
-
}
|
|
10
|
-
export interface Block {
|
|
11
|
-
timestamp: number;
|
|
12
|
-
txs: string[];
|
|
13
|
-
data: Uint8Array;
|
|
14
|
-
}
|
|
15
|
-
/** Tier-based dynamic tolerance (ms) — auditor-requested upgrade */
|
|
16
|
-
export declare const TIER_TOLERANCE_MS: Record<string, number>;
|
|
17
|
-
export declare class AdaptiveSwitch {
|
|
18
|
-
private windowSize;
|
|
19
|
-
private threshold;
|
|
20
|
-
private history;
|
|
21
|
-
private currentMode;
|
|
22
|
-
private minBlocks;
|
|
23
|
-
private penaltyCooldown;
|
|
24
|
-
private consecutiveFailures;
|
|
25
|
-
private turboEntryThreshold;
|
|
26
|
-
private turboMaintainThreshold;
|
|
27
|
-
private tolerance;
|
|
28
|
-
constructor(options?: {
|
|
29
|
-
tolerance?: number;
|
|
30
|
-
});
|
|
31
|
-
/**
|
|
32
|
-
* Core TTT mechanism: switches between Turbo/Full mode based on timestamp ordering match rate.
|
|
33
|
-
*/
|
|
34
|
-
verifyBlock(block: Block, tttRecord: TTTRecord, chainId: number, poolAddress: string, tier?: string): AdaptiveMode;
|
|
35
|
-
/**
|
|
36
|
-
* Return fee discount rate based on current mode.
|
|
37
|
-
* TURBO: 20% discount (incentivizes profitability).
|
|
38
|
-
* FULL: No discount.
|
|
39
|
-
*/
|
|
40
|
-
getFeeDiscount(): number;
|
|
41
|
-
/**
|
|
42
|
-
* Get current adaptive mode.
|
|
43
|
-
*/
|
|
44
|
-
getCurrentMode(): AdaptiveMode;
|
|
45
|
-
/**
|
|
46
|
-
* Reset history (for testing).
|
|
47
|
-
*/
|
|
48
|
-
reset(): void;
|
|
49
|
-
/**
|
|
50
|
-
* Serialize internal state to JSON for persistence across restarts.
|
|
51
|
-
* Allows operators to avoid re-learning over 20 blocks after a restart.
|
|
52
|
-
*/
|
|
53
|
-
serialize(): string;
|
|
54
|
-
/**
|
|
55
|
-
* Reconstruct an AdaptiveSwitch from previously serialized JSON state.
|
|
56
|
-
*/
|
|
57
|
-
static deserialize(json: string): AdaptiveSwitch;
|
|
58
|
-
private compareTransactionOrder;
|
|
59
|
-
}
|
package/dist/adaptive_switch.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// sdk/src/adaptive_switch.ts — Adaptive Mode Switcher
|
|
3
|
-
// Turbo (50ms) vs Full (127ms)
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.AdaptiveSwitch = exports.TIER_TOLERANCE_MS = exports.AdaptiveMode = void 0;
|
|
6
|
-
const grg_inverse_1 = require("./grg_inverse");
|
|
7
|
-
const logger_1 = require("./logger");
|
|
8
|
-
var AdaptiveMode;
|
|
9
|
-
(function (AdaptiveMode) {
|
|
10
|
-
AdaptiveMode["TURBO"] = "TURBO";
|
|
11
|
-
AdaptiveMode["FULL"] = "FULL";
|
|
12
|
-
})(AdaptiveMode || (exports.AdaptiveMode = AdaptiveMode = {}));
|
|
13
|
-
// const TOLERANCE = 100; // 100ms tolerance for GEO-sat operator sync (now configurable via constructor)
|
|
14
|
-
/** Tier-based dynamic tolerance (ms) — auditor-requested upgrade */
|
|
15
|
-
exports.TIER_TOLERANCE_MS = {
|
|
16
|
-
T0_epoch: 2000, // 6.4min tick → 2s tolerance
|
|
17
|
-
T1_block: 200, // 2s tick → 200ms
|
|
18
|
-
T2_slot: 500, // 12s tick → 500ms
|
|
19
|
-
T3_micro: 10, // 100ms tick → 10ms (10%)
|
|
20
|
-
};
|
|
21
|
-
class AdaptiveSwitch {
|
|
22
|
-
windowSize = 20; // B1-9: Updated from 10 to 20
|
|
23
|
-
threshold = 0.9; // B1-9: Updated from 0.8 to 0.9
|
|
24
|
-
history = [];
|
|
25
|
-
currentMode = AdaptiveMode.FULL;
|
|
26
|
-
minBlocks = 20; // B1-9: Minimum blocks for TURBO transition
|
|
27
|
-
penaltyCooldown = 0; // B1-9: Penalty cooldown (P2-1: increased to 20 + exponential backoff)
|
|
28
|
-
consecutiveFailures = 0; // P2-1: Track consecutive failures for exponential backoff
|
|
29
|
-
turboEntryThreshold = 0.95; // P2-2: Hysteresis — stricter entry
|
|
30
|
-
turboMaintainThreshold = 0.85; // P2-2: Hysteresis — relaxed maintenance
|
|
31
|
-
tolerance;
|
|
32
|
-
constructor(options) {
|
|
33
|
-
this.tolerance = options?.tolerance ?? 100;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Core TTT mechanism: switches between Turbo/Full mode based on timestamp ordering match rate.
|
|
37
|
-
*/
|
|
38
|
-
verifyBlock(block, tttRecord, chainId, poolAddress, tier) {
|
|
39
|
-
// 1. Check timestamp ordering and time match
|
|
40
|
-
const orderMatch = this.compareTransactionOrder(block.txs, tttRecord.txOrder);
|
|
41
|
-
const tolerance = tier ? (exports.TIER_TOLERANCE_MS[tier] ?? this.tolerance) : this.tolerance;
|
|
42
|
-
const timeMatch = Math.abs(block.timestamp - tttRecord.time) < tolerance;
|
|
43
|
-
let sequenceOk = orderMatch && timeMatch;
|
|
44
|
-
// B1-1: Do not skip GrgInverse.verify() in TURBO mode
|
|
45
|
-
// We check integrity regardless of mode
|
|
46
|
-
const integrityOk = grg_inverse_1.GrgInverse.verify(block.data, tttRecord.grgPayload, chainId, poolAddress);
|
|
47
|
-
if (!integrityOk) {
|
|
48
|
-
logger_1.logger.error(`[AdaptiveSwitch] GRG integrity check FAILED`);
|
|
49
|
-
sequenceOk = false; // Mark as false if integrity fails
|
|
50
|
-
if (this.currentMode === AdaptiveMode.TURBO) {
|
|
51
|
-
logger_1.logger.warn(`[AdaptiveSwitch] TURBO integrity failure: Penalty cooldown applied`);
|
|
52
|
-
// P2-1: Exponential backoff — 20 * 2^(consecutiveFailures), capped at 320
|
|
53
|
-
this.consecutiveFailures = Math.min(this.consecutiveFailures + 1, 4);
|
|
54
|
-
this.penaltyCooldown = 20 * Math.pow(2, this.consecutiveFailures - 1); // 20, 40, 80, 160, 320
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// 2. Update history (Sliding Window)
|
|
58
|
-
this.history.push(sequenceOk);
|
|
59
|
-
if (this.history.length > this.windowSize) {
|
|
60
|
-
this.history.shift();
|
|
61
|
-
}
|
|
62
|
-
if (this.penaltyCooldown > 0) {
|
|
63
|
-
this.penaltyCooldown--;
|
|
64
|
-
}
|
|
65
|
-
// 3. Calculate match rate and switch mode
|
|
66
|
-
const matchCount = this.history.filter(h => h).length;
|
|
67
|
-
const matchRate = this.history.length > 0 ? matchCount / this.history.length : 0;
|
|
68
|
-
// P2-2: Hysteresis — different thresholds for entering vs maintaining TURBO
|
|
69
|
-
const effectiveThreshold = this.currentMode === AdaptiveMode.TURBO
|
|
70
|
-
? this.turboMaintainThreshold // 85% to stay in TURBO
|
|
71
|
-
: this.turboEntryThreshold; // 95% to enter TURBO
|
|
72
|
-
if (this.history.length >= this.minBlocks && matchRate >= effectiveThreshold && this.penaltyCooldown === 0) {
|
|
73
|
-
if (this.currentMode === AdaptiveMode.FULL) {
|
|
74
|
-
logger_1.logger.info(`[AdaptiveSwitch] Switching to TURBO mode (Match rate: ${(matchRate * 100).toFixed(1)}%, Entry threshold: ${(this.turboEntryThreshold * 100).toFixed(0)}%)`);
|
|
75
|
-
}
|
|
76
|
-
this.currentMode = AdaptiveMode.TURBO;
|
|
77
|
-
this.consecutiveFailures = 0; // P2-1: Reset on successful TURBO
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
if (this.currentMode === AdaptiveMode.TURBO) {
|
|
81
|
-
logger_1.logger.warn(`[AdaptiveSwitch] Switching to FULL mode (Match rate: ${(matchRate * 100).toFixed(1)}%, Maintain threshold: ${(this.turboMaintainThreshold * 100).toFixed(0)}%, Cooldown: ${this.penaltyCooldown})`);
|
|
82
|
-
}
|
|
83
|
-
this.currentMode = AdaptiveMode.FULL;
|
|
84
|
-
}
|
|
85
|
-
return this.currentMode;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Return fee discount rate based on current mode.
|
|
89
|
-
* TURBO: 20% discount (incentivizes profitability).
|
|
90
|
-
* FULL: No discount.
|
|
91
|
-
*/
|
|
92
|
-
getFeeDiscount() {
|
|
93
|
-
return this.currentMode === AdaptiveMode.TURBO ? 0.2 : 0.0;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Get current adaptive mode.
|
|
97
|
-
*/
|
|
98
|
-
getCurrentMode() {
|
|
99
|
-
return this.currentMode;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Reset history (for testing).
|
|
103
|
-
*/
|
|
104
|
-
reset() {
|
|
105
|
-
this.history = [];
|
|
106
|
-
this.currentMode = AdaptiveMode.FULL;
|
|
107
|
-
this.penaltyCooldown = 0;
|
|
108
|
-
this.consecutiveFailures = 0;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Serialize internal state to JSON for persistence across restarts.
|
|
112
|
-
* Allows operators to avoid re-learning over 20 blocks after a restart.
|
|
113
|
-
*/
|
|
114
|
-
serialize() {
|
|
115
|
-
return JSON.stringify({
|
|
116
|
-
history: this.history,
|
|
117
|
-
currentMode: this.currentMode,
|
|
118
|
-
consecutiveFailures: this.consecutiveFailures,
|
|
119
|
-
penaltyCooldown: this.penaltyCooldown,
|
|
120
|
-
tolerance: this.tolerance,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Reconstruct an AdaptiveSwitch from previously serialized JSON state.
|
|
125
|
-
*/
|
|
126
|
-
static deserialize(json) {
|
|
127
|
-
const data = JSON.parse(json);
|
|
128
|
-
const instance = new AdaptiveSwitch({ tolerance: data.tolerance ?? 100 });
|
|
129
|
-
instance.history = data.history;
|
|
130
|
-
instance.currentMode = data.currentMode;
|
|
131
|
-
instance.consecutiveFailures = data.consecutiveFailures;
|
|
132
|
-
instance.penaltyCooldown = data.penaltyCooldown;
|
|
133
|
-
return instance;
|
|
134
|
-
}
|
|
135
|
-
compareTransactionOrder(blockTxs, expectedOrder) {
|
|
136
|
-
if (blockTxs.length !== expectedOrder.length)
|
|
137
|
-
return false;
|
|
138
|
-
for (let i = 0; i < blockTxs.length; i++) {
|
|
139
|
-
if (blockTxs[i] !== expectedOrder[i])
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
exports.AdaptiveSwitch = AdaptiveSwitch;
|
package/dist/auto_mint.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { TimeSynthesis } from "./time_synthesis";
|
|
2
|
-
import { EVMConnector } from "./evm_connector";
|
|
3
|
-
import { AutoMintConfig, MintResult } from "./types";
|
|
4
|
-
/**
|
|
5
|
-
* AutoMintEngine - Automatic TTT minting engine.
|
|
6
|
-
* Combines time synthesis, dynamic fee calculation, and EVM minting into a single loop.
|
|
7
|
-
*/
|
|
8
|
-
export declare class AutoMintEngine {
|
|
9
|
-
private config;
|
|
10
|
-
private timeSynthesis;
|
|
11
|
-
private feeEngine;
|
|
12
|
-
private evmConnector;
|
|
13
|
-
private feeCollector;
|
|
14
|
-
private timer;
|
|
15
|
-
private isRunning;
|
|
16
|
-
private isProcessing;
|
|
17
|
-
private onMintCallback?;
|
|
18
|
-
private onFailureCallback?;
|
|
19
|
-
private onLatencyCallback?;
|
|
20
|
-
private cachedSigner;
|
|
21
|
-
private consecutiveFailures;
|
|
22
|
-
private maxConsecutiveFailures;
|
|
23
|
-
private potSigner;
|
|
24
|
-
/** Monotonic counter appended to tokenId hash to prevent collision when two mints share the same nanosecond timestamp. */
|
|
25
|
-
private mintNonce;
|
|
26
|
-
/** Fire the GRG >50ms performance warning at most once per engine session. */
|
|
27
|
-
private warnedGrgSlow;
|
|
28
|
-
constructor(config: AutoMintConfig);
|
|
29
|
-
getEvmConnector(): EVMConnector;
|
|
30
|
-
getTimeSynthesis(): TimeSynthesis;
|
|
31
|
-
setOnMint(callback: (result: MintResult) => void): void;
|
|
32
|
-
setOnFailure(callback: (error: Error) => void): void;
|
|
33
|
-
setOnLatency(callback: (ms: number) => void): void;
|
|
34
|
-
/**
|
|
35
|
-
* Initialize the engine (RPC connection and contract setup).
|
|
36
|
-
*/
|
|
37
|
-
initialize(): Promise<void>;
|
|
38
|
-
/**
|
|
39
|
-
* Start the automatic minting loop.
|
|
40
|
-
*/
|
|
41
|
-
start(): void;
|
|
42
|
-
/**
|
|
43
|
-
* Stop the automatic minting loop.
|
|
44
|
-
*/
|
|
45
|
-
stop(): void;
|
|
46
|
-
/**
|
|
47
|
-
* Resume the minting loop after a circuit breaker trip.
|
|
48
|
-
* Resets the consecutive failure counter and restarts the loop.
|
|
49
|
-
*/
|
|
50
|
-
resume(): void;
|
|
51
|
-
/**
|
|
52
|
-
* Sleep helper for retry backoff.
|
|
53
|
-
*/
|
|
54
|
-
private sleep;
|
|
55
|
-
/**
|
|
56
|
-
* Execute a single mint tick.
|
|
57
|
-
* Time synthesis -> tokenId generation -> EVM mint call -> fee calculation/deduction.
|
|
58
|
-
*/
|
|
59
|
-
mintTick(): Promise<void>;
|
|
60
|
-
private signFeeMessage;
|
|
61
|
-
}
|
package/dist/auto_mint.js
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AutoMintEngine = void 0;
|
|
4
|
-
const ethers_1 = require("ethers");
|
|
5
|
-
const time_synthesis_1 = require("./time_synthesis");
|
|
6
|
-
const dynamic_fee_1 = require("./dynamic_fee");
|
|
7
|
-
const evm_connector_1 = require("./evm_connector");
|
|
8
|
-
const protocol_fee_1 = require("./protocol_fee");
|
|
9
|
-
const pot_signer_1 = require("./pot_signer");
|
|
10
|
-
const grg_forward_1 = require("./grg_forward");
|
|
11
|
-
const types_1 = require("./types");
|
|
12
|
-
const logger_1 = require("./logger");
|
|
13
|
-
const errors_1 = require("./errors");
|
|
14
|
-
/** Maximum retry attempts for RPC-dependent operations within a single tick */
|
|
15
|
-
const MINT_TICK_MAX_RETRIES = 3;
|
|
16
|
-
/** Backoff durations in ms for each retry attempt (1s, 2s, 4s) */
|
|
17
|
-
const MINT_TICK_BACKOFF_MS = [1000, 2000, 4000];
|
|
18
|
-
/**
|
|
19
|
-
* AutoMintEngine - Automatic TTT minting engine.
|
|
20
|
-
* Combines time synthesis, dynamic fee calculation, and EVM minting into a single loop.
|
|
21
|
-
*/
|
|
22
|
-
class AutoMintEngine {
|
|
23
|
-
config;
|
|
24
|
-
timeSynthesis;
|
|
25
|
-
feeEngine;
|
|
26
|
-
evmConnector;
|
|
27
|
-
feeCollector = null;
|
|
28
|
-
timer = null;
|
|
29
|
-
isRunning = false;
|
|
30
|
-
isProcessing = false;
|
|
31
|
-
onMintCallback;
|
|
32
|
-
onFailureCallback;
|
|
33
|
-
onLatencyCallback;
|
|
34
|
-
cachedSigner = null;
|
|
35
|
-
consecutiveFailures = 0;
|
|
36
|
-
maxConsecutiveFailures = 5;
|
|
37
|
-
potSigner = null;
|
|
38
|
-
/** Monotonic counter appended to tokenId hash to prevent collision when two mints share the same nanosecond timestamp. */
|
|
39
|
-
mintNonce = BigInt(Date.now());
|
|
40
|
-
/** Fire the GRG >50ms performance warning at most once per engine session. */
|
|
41
|
-
warnedGrgSlow = false;
|
|
42
|
-
constructor(config) {
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.timeSynthesis = new time_synthesis_1.TimeSynthesis({ sources: config.timeSources });
|
|
45
|
-
this.feeEngine = new dynamic_fee_1.DynamicFeeEngine({
|
|
46
|
-
cacheDurationMs: 5000,
|
|
47
|
-
fallbackPriceUsd: config.fallbackPriceUsd || 10000n,
|
|
48
|
-
});
|
|
49
|
-
this.evmConnector = new evm_connector_1.EVMConnector();
|
|
50
|
-
if (config.signer) {
|
|
51
|
-
this.cachedSigner = config.signer;
|
|
52
|
-
}
|
|
53
|
-
// Initialize Ed25519 PoT signer for non-repudiation
|
|
54
|
-
this.potSigner = config.potSignerKeyPath
|
|
55
|
-
? pot_signer_1.PotSigner.createOrLoad(config.potSignerKeyPath)
|
|
56
|
-
: new pot_signer_1.PotSigner();
|
|
57
|
-
}
|
|
58
|
-
getEvmConnector() {
|
|
59
|
-
return this.evmConnector;
|
|
60
|
-
}
|
|
61
|
-
getTimeSynthesis() {
|
|
62
|
-
return this.timeSynthesis;
|
|
63
|
-
}
|
|
64
|
-
setOnMint(callback) {
|
|
65
|
-
this.onMintCallback = callback;
|
|
66
|
-
}
|
|
67
|
-
setOnFailure(callback) {
|
|
68
|
-
this.onFailureCallback = callback;
|
|
69
|
-
}
|
|
70
|
-
setOnLatency(callback) {
|
|
71
|
-
this.onLatencyCallback = callback;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Initialize the engine (RPC connection and contract setup).
|
|
75
|
-
*/
|
|
76
|
-
async initialize() {
|
|
77
|
-
try {
|
|
78
|
-
const signerOrKey = this.config.signer || this.config.privateKey;
|
|
79
|
-
if (!signerOrKey)
|
|
80
|
-
throw new errors_1.TTTConfigError(errors_1.ERROR_CODES.CONFIG_MISSING_SIGNER, "[AutoMint] Signer or Private Key is required", "Missing both 'signer' and 'privateKey' in config", "Provide a valid ethers.Signer or a private key string in your configuration.");
|
|
81
|
-
await this.evmConnector.connect(this.config.rpcUrl, signerOrKey);
|
|
82
|
-
await this.feeEngine.connect(this.config.rpcUrl);
|
|
83
|
-
this.cachedSigner = this.evmConnector.getSigner();
|
|
84
|
-
const tttAbi = [
|
|
85
|
-
"function mint(address to, uint256 amount, bytes32 grgHash) external returns (bool)",
|
|
86
|
-
"function burn(uint256 amount, bytes32 grgHash, uint256 tier) external",
|
|
87
|
-
"function balanceOf(address account, uint256 id) external view returns (uint256)",
|
|
88
|
-
"event TTTMinted(address indexed to, uint256 indexed tokenId, uint256 amount)",
|
|
89
|
-
"event TTTBurned(address indexed from, uint256 indexed tokenId, uint256 amount, uint256 tier)",
|
|
90
|
-
// CT Log equivalent: every PoT is publicly auditable on-chain
|
|
91
|
-
evm_connector_1.EVMConnector.POT_ANCHORED_EVENT_ABI
|
|
92
|
-
];
|
|
93
|
-
this.evmConnector.attachContract(this.config.contractAddress, tttAbi);
|
|
94
|
-
if (this.config.feeCollectorAddress) {
|
|
95
|
-
this.feeCollector = new protocol_fee_1.ProtocolFeeCollector(this.config.chainId, this.config.feeCollectorAddress, this.evmConnector, this.config.protocolFeeRecipient);
|
|
96
|
-
await this.feeCollector.validateChainId();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
// State rollback: Ensure connections are closed or reset
|
|
101
|
-
this.evmConnector = new evm_connector_1.EVMConnector();
|
|
102
|
-
this.cachedSigner = null;
|
|
103
|
-
this.feeCollector = null;
|
|
104
|
-
logger_1.logger.error(`[AutoMint] Initialization failed, state rolled back: ${error instanceof Error ? error.message : error}`);
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Start the automatic minting loop.
|
|
110
|
-
*/
|
|
111
|
-
start() {
|
|
112
|
-
if (this.isRunning)
|
|
113
|
-
return;
|
|
114
|
-
// Clear existing timer if any
|
|
115
|
-
if (this.timer) {
|
|
116
|
-
clearInterval(this.timer);
|
|
117
|
-
this.timer = null;
|
|
118
|
-
}
|
|
119
|
-
const interval = types_1.TierIntervals[this.config.tier];
|
|
120
|
-
this.isRunning = true;
|
|
121
|
-
this.timer = setInterval(async () => {
|
|
122
|
-
if (this.isProcessing)
|
|
123
|
-
return;
|
|
124
|
-
this.isProcessing = true;
|
|
125
|
-
const tickStart = Date.now();
|
|
126
|
-
try {
|
|
127
|
-
await this.mintTick();
|
|
128
|
-
this.consecutiveFailures = 0;
|
|
129
|
-
// H2: Report latency to TTTClient
|
|
130
|
-
if (this.onLatencyCallback) {
|
|
131
|
-
this.onLatencyCallback(Date.now() - tickStart);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
this.consecutiveFailures++;
|
|
136
|
-
// H2: Report failure to TTTClient
|
|
137
|
-
if (this.onFailureCallback) {
|
|
138
|
-
this.onFailureCallback(error instanceof Error ? error : new Error(String(error)));
|
|
139
|
-
}
|
|
140
|
-
logger_1.logger.error(`[AutoMint] Tick execution failed (${this.consecutiveFailures}/${this.maxConsecutiveFailures}): ${error instanceof Error ? error.message : error}`);
|
|
141
|
-
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
|
|
142
|
-
logger_1.logger.error(`[AutoMint] Circuit breaker triggered: ${this.consecutiveFailures} consecutive failures. Stopping engine to prevent DoS.`);
|
|
143
|
-
this.stop();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
finally {
|
|
147
|
-
this.isProcessing = false;
|
|
148
|
-
}
|
|
149
|
-
}, interval);
|
|
150
|
-
logger_1.logger.info(`[AutoMint] Loop started for tier ${this.config.tier} (${interval}ms)`);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Stop the automatic minting loop.
|
|
154
|
-
*/
|
|
155
|
-
stop() {
|
|
156
|
-
if (this.timer) {
|
|
157
|
-
clearInterval(this.timer);
|
|
158
|
-
this.timer = null;
|
|
159
|
-
}
|
|
160
|
-
this.isRunning = false;
|
|
161
|
-
logger_1.logger.info(`[AutoMint] Loop stopped`);
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Resume the minting loop after a circuit breaker trip.
|
|
165
|
-
* Resets the consecutive failure counter and restarts the loop.
|
|
166
|
-
*/
|
|
167
|
-
resume() {
|
|
168
|
-
this.consecutiveFailures = 0;
|
|
169
|
-
logger_1.logger.info(`[AutoMint] Consecutive failures reset, resuming...`);
|
|
170
|
-
this.start();
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Sleep helper for retry backoff.
|
|
174
|
-
*/
|
|
175
|
-
sleep(ms) {
|
|
176
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Execute a single mint tick.
|
|
180
|
-
* Time synthesis -> tokenId generation -> EVM mint call -> fee calculation/deduction.
|
|
181
|
-
*/
|
|
182
|
-
async mintTick() {
|
|
183
|
-
// 1. Time Synthesis
|
|
184
|
-
const synthesized = await this.timeSynthesis.synthesize();
|
|
185
|
-
if (!synthesized) {
|
|
186
|
-
logger_1.logger.warn(`[AutoMint] Time synthesis returned null/undefined, skipping tick`);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
// Fix-12: Integrity check
|
|
190
|
-
if (synthesized.confidence === 0 || synthesized.stratum >= 16) {
|
|
191
|
-
throw new errors_1.TTTTimeSynthesisError(errors_1.ERROR_CODES.TIME_SYNTHESIS_INTEGRITY_FAILED, `[AutoMint] Synthesis integrity check failed`, `confidence=${synthesized.confidence}, stratum=${synthesized.stratum}`, `Check NTP sources or network connectivity.`);
|
|
192
|
-
}
|
|
193
|
-
// 1-1. PoT Generation & Validation (W1-1)
|
|
194
|
-
const pot = await this.timeSynthesis.generateProofOfTime();
|
|
195
|
-
if (pot.confidence < 0.5) {
|
|
196
|
-
throw new errors_1.TTTTimeSynthesisError(errors_1.ERROR_CODES.TIME_SYNTHESIS_INSUFFICIENT_CONFIDENCE, `[PoT] Insufficient confidence`, `Calculated confidence ${pot.confidence} is below required 0.5`, `Ensure more NTP sources are reachable or decrease uncertainty.`);
|
|
197
|
-
}
|
|
198
|
-
// Deterministic potHash via ABI.encode — field order is fixed,
|
|
199
|
-
// independent of JS engine key ordering. External verifiers can
|
|
200
|
-
// reproduce this hash from the same PoT fields.
|
|
201
|
-
const nonceHash = ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(pot.nonce));
|
|
202
|
-
const potHash = ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["uint64", "uint64", "uint8", "uint8", "uint32", "bytes32"], [
|
|
203
|
-
pot.timestamp,
|
|
204
|
-
pot.expiresAt,
|
|
205
|
-
pot.sources,
|
|
206
|
-
pot.stratum,
|
|
207
|
-
Math.round(pot.confidence * 1_000_000),
|
|
208
|
-
nonceHash
|
|
209
|
-
]));
|
|
210
|
-
// 1-2. Ed25519 issuer signature for non-repudiation
|
|
211
|
-
if (this.potSigner) {
|
|
212
|
-
pot.issuerSignature = this.potSigner.signPot(potHash);
|
|
213
|
-
logger_1.logger.info(`[AutoMint] PoT signed by issuer ${this.potSigner.getPubKeyHex().substring(0, 16)}...`);
|
|
214
|
-
}
|
|
215
|
-
// 2. Generate tokenId (keccak256)
|
|
216
|
-
// Unique ID based on chainId, poolAddress, timestamp, and a monotonic nonce
|
|
217
|
-
// to prevent collision if two mints occur at the same nanosecond timestamp.
|
|
218
|
-
const nonceSuffix = this.mintNonce;
|
|
219
|
-
this.mintNonce++;
|
|
220
|
-
const tokenId = ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "address", "uint64", "uint256"], [BigInt(this.config.chainId), this.config.poolAddress, synthesized.timestamp, nonceSuffix]));
|
|
221
|
-
// 3. Fee calculation
|
|
222
|
-
const feeCalculation = await this.feeEngine.calculateMintFee(this.config.tier);
|
|
223
|
-
// 4. EVM mint call — run GRG integrity pipeline
|
|
224
|
-
const grgPayload = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["bytes32", "bytes32", "uint64", "uint8"], [tokenId, potHash, synthesized.timestamp, pot.sources]);
|
|
225
|
-
const grgStart = Date.now();
|
|
226
|
-
const grgShards = grg_forward_1.GrgForward.encode(ethers_1.ethers.getBytes(grgPayload), this.config.chainId, this.config.poolAddress);
|
|
227
|
-
const grgElapsed = Date.now() - grgStart;
|
|
228
|
-
logger_1.logger.info(`[AutoMint] GRG pipeline completed in ${grgElapsed}ms`);
|
|
229
|
-
if (grgElapsed > 50 && !this.warnedGrgSlow) {
|
|
230
|
-
this.warnedGrgSlow = true;
|
|
231
|
-
logger_1.logger.warn(`[AutoMint] GRG pipeline took ${grgElapsed}ms (>50ms threshold). Consider offloading to a Worker Thread for T3_micro tiers.`);
|
|
232
|
-
}
|
|
233
|
-
// On-chain hash = keccak256 of concatenated GRG-encoded shards
|
|
234
|
-
const grgHash = ethers_1.ethers.keccak256(ethers_1.ethers.concat(grgShards));
|
|
235
|
-
// Recipient address (defaults to signer address)
|
|
236
|
-
if (!this.cachedSigner) {
|
|
237
|
-
throw new errors_1.TTTSignerError(errors_1.ERROR_CODES.SIGNER_NOT_INITIALIZED, "[AutoMint] Signer not initialized", "cachedSigner is null", "Initialize the engine before calling mintTick().");
|
|
238
|
-
}
|
|
239
|
-
const recipient = await this.cachedSigner.getAddress();
|
|
240
|
-
logger_1.logger.info(`[AutoMint] Executing mint: tokenId=${tokenId.substring(0, 10)}... amount=${feeCalculation.tttAmount}`);
|
|
241
|
-
// Retry loop for RPC-dependent mint operation (max 3 attempts, backoff 1s/2s/4s)
|
|
242
|
-
let receipt;
|
|
243
|
-
let lastError = null;
|
|
244
|
-
for (let attempt = 0; attempt < MINT_TICK_MAX_RETRIES; attempt++) {
|
|
245
|
-
try {
|
|
246
|
-
receipt = await this.evmConnector.mintTTT(recipient, feeCalculation.tttAmount, grgHash, potHash);
|
|
247
|
-
lastError = null;
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
252
|
-
if (attempt < MINT_TICK_MAX_RETRIES - 1) {
|
|
253
|
-
const backoff = MINT_TICK_BACKOFF_MS[attempt];
|
|
254
|
-
logger_1.logger.warn(`[AutoMint] Mint attempt ${attempt + 1}/${MINT_TICK_MAX_RETRIES} failed, retrying in ${backoff}ms: ${lastError.message}`);
|
|
255
|
-
await this.sleep(backoff);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (lastError || !receipt) {
|
|
260
|
-
throw lastError || new Error("[AutoMint] Mint failed after all retries");
|
|
261
|
-
}
|
|
262
|
-
// 5. Fee deduction/recording (handled by contract or tracked at SDK level)
|
|
263
|
-
// W2-3: Actual ProtocolFeeCollector call
|
|
264
|
-
let actualFeePaid = feeCalculation.protocolFeeUsd;
|
|
265
|
-
if (this.feeCollector && this.config.feeCollectorAddress) {
|
|
266
|
-
try {
|
|
267
|
-
// NOTE: Single-threaded JS guarantees atomicity between nonce generation,
|
|
268
|
-
// signing, and collection below. If running multiple AutoMintEngine instances
|
|
269
|
-
// (e.g., worker_threads or cluster), a separate nonce manager with locking is required.
|
|
270
|
-
// Sequential nonce: query contract for current nonce, matching ProtocolFee.sol's require(nonces[msg.sender] == nonce)
|
|
271
|
-
const feeContract = new ethers_1.ethers.Contract(this.config.feeCollectorAddress, ["function getNonce(address) external view returns (uint256)"], this.evmConnector.getProvider());
|
|
272
|
-
const nonce = BigInt(await feeContract.getNonce(recipient));
|
|
273
|
-
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour validity
|
|
274
|
-
const signature = await this.signFeeMessage(feeCalculation, nonce, deadline);
|
|
275
|
-
const user = recipient;
|
|
276
|
-
await this.feeCollector.collectMintFee(feeCalculation, signature, user, nonce, deadline);
|
|
277
|
-
}
|
|
278
|
-
catch (feeError) {
|
|
279
|
-
logger_1.logger.error(`[AutoMint] Fee collection failed but mint was successful: ${feeError instanceof Error ? feeError.message : feeError}`);
|
|
280
|
-
// Reset to 0 so downstream (onMint callback, ledger) does not record a fee that was never collected
|
|
281
|
-
actualFeePaid = 0n;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// CT Log equivalent: log PoT anchor info for subgraph indexers
|
|
285
|
-
logger_1.logger.info(`[AutoMint] PoTAnchored: timestamp=${synthesized.timestamp}, grgHash=${grgHash}, stratum=${synthesized.stratum}, potHash=${potHash}`);
|
|
286
|
-
logger_1.logger.info(`[AutoMint] Mint success: tx=${receipt.hash}, feePaid=${actualFeePaid} (USDC eq)`);
|
|
287
|
-
if (this.onMintCallback) {
|
|
288
|
-
this.onMintCallback({
|
|
289
|
-
tokenId: tokenId,
|
|
290
|
-
grgHash: grgHash,
|
|
291
|
-
timestamp: synthesized.timestamp,
|
|
292
|
-
txHash: receipt.hash,
|
|
293
|
-
protocolFeePaid: actualFeePaid,
|
|
294
|
-
proofOfTime: pot
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
async signFeeMessage(feeCalc, nonce, deadline) {
|
|
299
|
-
if (!this.cachedSigner) {
|
|
300
|
-
throw new errors_1.TTTSignerError(errors_1.ERROR_CODES.SIGNER_NOT_INITIALIZED, "[AutoMint] Signer not initialized", "cachedSigner is null", "Ensure initialize() was called successfully.");
|
|
301
|
-
}
|
|
302
|
-
// Type casting to handle signTypedData if available on the signer (Wallet supports it)
|
|
303
|
-
const signer = this.cachedSigner;
|
|
304
|
-
if (typeof signer.signTypedData !== 'function') {
|
|
305
|
-
throw new errors_1.TTTSignerError(errors_1.ERROR_CODES.SIGNER_NO_EIP712, "[AutoMint] Provided signer does not support signTypedData (EIP-712)", `Signer type ${signer.constructor.name} missing signTypedData`, "Use a Wallet or a signer that implements EIP-712 signTypedData.");
|
|
306
|
-
}
|
|
307
|
-
const domain = {
|
|
308
|
-
name: "OpenTTT_ProtocolFee",
|
|
309
|
-
version: "1",
|
|
310
|
-
chainId: this.config.chainId,
|
|
311
|
-
verifyingContract: this.config.feeCollectorAddress
|
|
312
|
-
};
|
|
313
|
-
const types = {
|
|
314
|
-
CollectFee: [
|
|
315
|
-
{ name: "token", type: "address" },
|
|
316
|
-
{ name: "amount", type: "uint256" },
|
|
317
|
-
{ name: "nonce", type: "uint256" },
|
|
318
|
-
{ name: "deadline", type: "uint256" }
|
|
319
|
-
]
|
|
320
|
-
};
|
|
321
|
-
const value = {
|
|
322
|
-
token: ethers_1.ethers.getAddress(feeCalc.feeTokenAddress),
|
|
323
|
-
amount: feeCalc.protocolFeeUsd,
|
|
324
|
-
nonce: nonce,
|
|
325
|
-
deadline: deadline
|
|
326
|
-
};
|
|
327
|
-
return await signer.signTypedData(domain, types, value);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
exports.AutoMintEngine = AutoMintEngine;
|
package/dist/golay.d.ts
DELETED
package/dist/golay.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// sdk/src/golay.ts
|
|
3
|
-
// Golay(24,12) Extended Binary Golay Code
|
|
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
|
-
const P = [
|
|
12
|
-
0xC75, 0x49F, 0xD4B, 0x6E3, 0x9B3, 0xB66,
|
|
13
|
-
0xECC, 0x1ED, 0x3DA, 0x7B4, 0xB1D, 0xE3A
|
|
14
|
-
];
|
|
15
|
-
// P^T (transpose of P) — required for second syndrome computation.
|
|
16
|
-
// P is NOT symmetric, so s2 = s*P^T must use this separate matrix.
|
|
17
|
-
const PT = [
|
|
18
|
-
0xAE3, 0xF25, 0x16F, 0x2DE, 0x5BC, 0xB78,
|
|
19
|
-
0x9D5, 0xC8F, 0x63B, 0xC76, 0x7C9, 0xF92
|
|
20
|
-
];
|
|
21
|
-
const UNIT_VECTORS = [
|
|
22
|
-
0x800, 0x400, 0x200, 0x100, 0x080, 0x040,
|
|
23
|
-
0x020, 0x010, 0x008, 0x004, 0x002, 0x001
|
|
24
|
-
];
|
|
25
|
-
function weight(n) {
|
|
26
|
-
let count = 0;
|
|
27
|
-
let temp = n & 0xFFF;
|
|
28
|
-
while (temp > 0) {
|
|
29
|
-
temp &= (temp - 1);
|
|
30
|
-
count++;
|
|
31
|
-
}
|
|
32
|
-
return count;
|
|
33
|
-
}
|
|
34
|
-
function multiplyP(v) {
|
|
35
|
-
let res = 0;
|
|
36
|
-
for (let i = 0; i < 12; i++) {
|
|
37
|
-
if ((v >> (11 - i)) & 1) {
|
|
38
|
-
res ^= P[i];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return res & 0xFFF;
|
|
42
|
-
}
|
|
43
|
-
function multiplyPT(v) {
|
|
44
|
-
let res = 0;
|
|
45
|
-
for (let i = 0; i < 12; i++) {
|
|
46
|
-
if ((v >> (11 - i)) & 1) {
|
|
47
|
-
res ^= PT[i];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return res & 0xFFF;
|
|
51
|
-
}
|
|
52
|
-
function encodeWord(msg) {
|
|
53
|
-
const parity = multiplyP(msg & 0xFFF);
|
|
54
|
-
return ((msg & 0xFFF) << 12) | parity;
|
|
55
|
-
}
|
|
56
|
-
function decodeWord(received) {
|
|
57
|
-
let r_m = (received >> 12) & 0xFFF;
|
|
58
|
-
const r_p = received & 0xFFF;
|
|
59
|
-
const s = multiplyP(r_m) ^ r_p;
|
|
60
|
-
if (s === 0)
|
|
61
|
-
return { msg: r_m, corrected: 0, uncorrectable: false };
|
|
62
|
-
if (weight(s) <= 3) {
|
|
63
|
-
return { msg: r_m, corrected: weight(s), uncorrectable: false };
|
|
64
|
-
}
|
|
65
|
-
for (let i = 0; i < 12; i++) {
|
|
66
|
-
if (weight(s ^ P[i]) <= 2) {
|
|
67
|
-
return { msg: r_m ^ UNIT_VECTORS[i], corrected: weight(s ^ P[i]) + 1, uncorrectable: false };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const s2 = multiplyPT(s);
|
|
71
|
-
if (weight(s2) <= 3) {
|
|
72
|
-
return { msg: r_m ^ s2, corrected: weight(s2), uncorrectable: false };
|
|
73
|
-
}
|
|
74
|
-
for (let i = 0; i < 12; i++) {
|
|
75
|
-
if (weight(s2 ^ PT[i]) <= 2) {
|
|
76
|
-
const error_m = s2 ^ PT[i];
|
|
77
|
-
return { msg: r_m ^ error_m, corrected: weight(s2 ^ PT[i]) + 1, uncorrectable: false };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return { msg: r_m, corrected: 0, uncorrectable: true };
|
|
81
|
-
}
|
|
82
|
-
function golayEncode(data) {
|
|
83
|
-
const out = new Uint8Array(Math.ceil(data.length / 3) * 6);
|
|
84
|
-
let outIdx = 0;
|
|
85
|
-
for (let i = 0; i < data.length; i += 3) {
|
|
86
|
-
const b1 = data[i];
|
|
87
|
-
const b2 = i + 1 < data.length ? data[i + 1] : 0;
|
|
88
|
-
const b3 = i + 2 < data.length ? data[i + 2] : 0;
|
|
89
|
-
const w1 = (b1 << 4) | (b2 >> 4);
|
|
90
|
-
const c1 = encodeWord(w1);
|
|
91
|
-
out[outIdx++] = (c1 >> 16) & 0xFF;
|
|
92
|
-
out[outIdx++] = (c1 >> 8) & 0xFF;
|
|
93
|
-
out[outIdx++] = c1 & 0xFF;
|
|
94
|
-
if (i + 1 < data.length) {
|
|
95
|
-
const w2 = ((b2 & 0x0F) << 8) | b3;
|
|
96
|
-
const c2 = encodeWord(w2);
|
|
97
|
-
out[outIdx++] = (c2 >> 16) & 0xFF;
|
|
98
|
-
out[outIdx++] = (c2 >> 8) & 0xFF;
|
|
99
|
-
out[outIdx++] = c2 & 0xFF;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return out;
|
|
103
|
-
}
|
|
104
|
-
function golayDecode(encoded) {
|
|
105
|
-
if (encoded.length % 6 !== 0) {
|
|
106
|
-
throw new Error("Invalid Golay encoded data: length must be multiple of 6");
|
|
107
|
-
}
|
|
108
|
-
const outLen = Math.floor(encoded.length / 2);
|
|
109
|
-
const out = new Uint8Array(outLen);
|
|
110
|
-
let outIdx = 0;
|
|
111
|
-
let totalCorrected = 0;
|
|
112
|
-
let anyUncorrectable = false;
|
|
113
|
-
for (let i = 0; i < encoded.length; i += 6) {
|
|
114
|
-
const c1 = (encoded[i] << 16) | (encoded[i + 1] << 8) | encoded[i + 2];
|
|
115
|
-
const res1 = decodeWord(c1);
|
|
116
|
-
totalCorrected += res1.corrected;
|
|
117
|
-
if (res1.uncorrectable)
|
|
118
|
-
anyUncorrectable = true;
|
|
119
|
-
if (outIdx < outLen)
|
|
120
|
-
out[outIdx++] = (res1.msg >> 4) & 0xFF;
|
|
121
|
-
const b2_high = (res1.msg & 0x0F) << 4;
|
|
122
|
-
if (i + 3 < encoded.length) {
|
|
123
|
-
const c2 = (encoded[i + 3] << 16) | (encoded[i + 4] << 8) | encoded[i + 5];
|
|
124
|
-
const res2 = decodeWord(c2);
|
|
125
|
-
totalCorrected += res2.corrected;
|
|
126
|
-
if (res2.uncorrectable)
|
|
127
|
-
anyUncorrectable = true;
|
|
128
|
-
if (outIdx < outLen)
|
|
129
|
-
out[outIdx++] = b2_high | (res2.msg >> 8);
|
|
130
|
-
if (outIdx < outLen)
|
|
131
|
-
out[outIdx++] = res2.msg & 0xFF;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
if (outIdx < outLen)
|
|
135
|
-
out[outIdx++] = b2_high;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return { data: out, corrected: totalCorrected, uncorrectable: anyUncorrectable };
|
|
139
|
-
}
|
package/dist/grg_forward.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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 deriveHmacKey(chainId: number, poolAddress: string): Buffer;
|
|
5
|
-
static golayEncodeWrapper(data: Uint8Array, hmacKey: Buffer): Uint8Array;
|
|
6
|
-
static encode(data: Uint8Array, chainId: number, poolAddress: string): Uint8Array[];
|
|
7
|
-
}
|
package/dist/grg_forward.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GrgForward = void 0;
|
|
4
|
-
// sdk/src/grg_forward.ts
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const ethers_1 = require("ethers");
|
|
7
|
-
const golay_1 = require("./golay");
|
|
8
|
-
const reed_solomon_1 = require("./reed_solomon");
|
|
9
|
-
class GrgForward {
|
|
10
|
-
static golombEncode(data, m = 16) {
|
|
11
|
-
if (m < 2)
|
|
12
|
-
throw new Error("[GRG] Golomb parameter m must be >= 2");
|
|
13
|
-
const k = Math.log2(m);
|
|
14
|
-
if (!Number.isInteger(k))
|
|
15
|
-
throw new Error("M must be power of 2");
|
|
16
|
-
const bits = [];
|
|
17
|
-
for (const byte of data) {
|
|
18
|
-
const q = Math.floor(byte / m);
|
|
19
|
-
const r = byte % m;
|
|
20
|
-
for (let i = 0; i < q; i++)
|
|
21
|
-
bits.push(1);
|
|
22
|
-
bits.push(0);
|
|
23
|
-
for (let i = k - 1; i >= 0; i--)
|
|
24
|
-
bits.push((r >> i) & 1);
|
|
25
|
-
}
|
|
26
|
-
const out = new Uint8Array(Math.ceil(bits.length / 8));
|
|
27
|
-
for (let i = 0; i < bits.length; i++) {
|
|
28
|
-
if (bits[i])
|
|
29
|
-
out[i >> 3] |= (0x80 >> (i & 7));
|
|
30
|
-
}
|
|
31
|
-
return out;
|
|
32
|
-
}
|
|
33
|
-
static redstuffEncode(data, shards = 4, parity = 2) {
|
|
34
|
-
return reed_solomon_1.ReedSolomon.encode(data, shards, parity);
|
|
35
|
-
}
|
|
36
|
-
static deriveHmacKey(chainId, poolAddress) {
|
|
37
|
-
if (chainId === undefined || chainId === null || !poolAddress) {
|
|
38
|
-
throw new Error("[GRG] chainId and poolAddress are required for HMAC key derivation. No default key is allowed.");
|
|
39
|
-
}
|
|
40
|
-
const packed = (0, ethers_1.keccak256)(ethers_1.AbiCoder.defaultAbiCoder().encode(["uint256", "address"], [chainId, poolAddress]));
|
|
41
|
-
return Buffer.from(packed.slice(2), "hex");
|
|
42
|
-
}
|
|
43
|
-
static golayEncodeWrapper(data, hmacKey) {
|
|
44
|
-
const encoded = (0, golay_1.golayEncode)(data);
|
|
45
|
-
const mac = (0, crypto_1.createHmac)("sha256", hmacKey).update(Buffer.from(encoded)).digest();
|
|
46
|
-
const checksum = mac.subarray(0, 8);
|
|
47
|
-
const final = new Uint8Array(encoded.length + 8);
|
|
48
|
-
final.set(encoded);
|
|
49
|
-
final.set(checksum, encoded.length);
|
|
50
|
-
return final;
|
|
51
|
-
}
|
|
52
|
-
static encode(data, chainId, poolAddress) {
|
|
53
|
-
if (data.length === 0) {
|
|
54
|
-
throw new Error("[GRG] Cannot encode empty input — roundtrip identity violation");
|
|
55
|
-
}
|
|
56
|
-
const compressed = this.golombEncode(data);
|
|
57
|
-
const withLen = new Uint8Array(4 + compressed.length);
|
|
58
|
-
withLen[0] = (data.length >> 24) & 0xFF;
|
|
59
|
-
withLen[1] = (data.length >> 16) & 0xFF;
|
|
60
|
-
withLen[2] = (data.length >> 8) & 0xFF;
|
|
61
|
-
withLen[3] = data.length & 0xFF;
|
|
62
|
-
withLen.set(compressed, 4);
|
|
63
|
-
const shards = this.redstuffEncode(withLen);
|
|
64
|
-
const hmacKey = this.deriveHmacKey(chainId, poolAddress);
|
|
65
|
-
return shards.map(s => this.golayEncodeWrapper(s, hmacKey));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
exports.GrgForward = GrgForward;
|
package/dist/grg_inverse.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare class GrgInverse {
|
|
2
|
-
private static readonly MAX_GOLOMB_Q;
|
|
3
|
-
static golayDecodeWrapper(data: Uint8Array, hmacKey: Buffer): Uint8Array;
|
|
4
|
-
static redstuffDecode(shards: (Uint8Array | null)[], dataShardCount?: number, parityShardCount?: number): Uint8Array;
|
|
5
|
-
static golombDecode(data: Uint8Array, m?: number, originalLength?: number): Uint8Array;
|
|
6
|
-
static verify(data: Uint8Array, originalShards: Uint8Array[], chainId: number, poolAddress: string): boolean;
|
|
7
|
-
}
|
package/dist/grg_inverse.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GrgInverse = void 0;
|
|
4
|
-
// sdk/src/grg_inverse.ts
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const golay_1 = require("./golay");
|
|
7
|
-
const grg_forward_1 = require("./grg_forward");
|
|
8
|
-
const logger_1 = require("./logger");
|
|
9
|
-
const reed_solomon_1 = require("./reed_solomon");
|
|
10
|
-
class GrgInverse {
|
|
11
|
-
static MAX_GOLOMB_Q = 1000000;
|
|
12
|
-
static golayDecodeWrapper(data, hmacKey) {
|
|
13
|
-
if (data.length < 8)
|
|
14
|
-
throw new Error("GRG shard too short for checksum");
|
|
15
|
-
const encoded = data.subarray(0, data.length - 8);
|
|
16
|
-
const checksum = data.subarray(data.length - 8);
|
|
17
|
-
const mac = (0, crypto_1.createHmac)("sha256", hmacKey).update(Buffer.from(encoded)).digest();
|
|
18
|
-
const expected = mac.subarray(0, 8);
|
|
19
|
-
if (!Buffer.from(checksum).equals(Buffer.from(expected))) {
|
|
20
|
-
throw new Error("GRG tamper detected: HMAC-SHA256 checksum mismatch");
|
|
21
|
-
}
|
|
22
|
-
const res = (0, golay_1.golayDecode)(encoded);
|
|
23
|
-
if (res.uncorrectable) {
|
|
24
|
-
throw new Error("GRG tamper detected: uncorrectable bit errors in Golay codeword");
|
|
25
|
-
}
|
|
26
|
-
return res.data;
|
|
27
|
-
}
|
|
28
|
-
static redstuffDecode(shards, dataShardCount = 4, parityShardCount = 2) {
|
|
29
|
-
return reed_solomon_1.ReedSolomon.decode(shards, dataShardCount, parityShardCount);
|
|
30
|
-
}
|
|
31
|
-
static golombDecode(data, m = 16, originalLength) {
|
|
32
|
-
if (m < 2)
|
|
33
|
-
throw new Error("[GRG] Golomb parameter m must be >= 2");
|
|
34
|
-
const k = Math.log2(m);
|
|
35
|
-
const totalBits = data.length * 8;
|
|
36
|
-
const readBit = (pos) => (data[pos >> 3] >> (7 - (pos & 7))) & 1;
|
|
37
|
-
const result = [];
|
|
38
|
-
let i = 0;
|
|
39
|
-
while (i < totalBits) {
|
|
40
|
-
if (originalLength !== undefined && result.length >= originalLength)
|
|
41
|
-
break;
|
|
42
|
-
let q = 0;
|
|
43
|
-
while (i < totalBits && readBit(i) === 1) {
|
|
44
|
-
q++;
|
|
45
|
-
i++;
|
|
46
|
-
if (q > this.MAX_GOLOMB_Q)
|
|
47
|
-
throw new Error(`[GRG] Golomb decode: unary run exceeds ${this.MAX_GOLOMB_Q} — malformed or malicious input`);
|
|
48
|
-
}
|
|
49
|
-
if (i < totalBits && readBit(i) === 0)
|
|
50
|
-
i++;
|
|
51
|
-
if (i + k > totalBits)
|
|
52
|
-
break;
|
|
53
|
-
let r = 0;
|
|
54
|
-
for (let j = 0; j < k; j++)
|
|
55
|
-
r = (r << 1) | readBit(i + j);
|
|
56
|
-
result.push(q * m + r);
|
|
57
|
-
i += k;
|
|
58
|
-
}
|
|
59
|
-
return new Uint8Array(result);
|
|
60
|
-
}
|
|
61
|
-
static verify(data, originalShards, chainId, poolAddress) {
|
|
62
|
-
try {
|
|
63
|
-
const hmacKey = grg_forward_1.GrgForward.deriveHmacKey(chainId, poolAddress);
|
|
64
|
-
const decodedShards = originalShards.map(s => {
|
|
65
|
-
try {
|
|
66
|
-
return this.golayDecodeWrapper(s, hmacKey);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
const withLen = this.redstuffDecode(decodedShards);
|
|
73
|
-
if (withLen.length < 4)
|
|
74
|
-
return false;
|
|
75
|
-
const origLen = (withLen[0] << 24) | (withLen[1] << 16) | (withLen[2] << 8) | withLen[3];
|
|
76
|
-
const compressed = withLen.subarray(4);
|
|
77
|
-
const decoded = this.golombDecode(compressed);
|
|
78
|
-
const final = decoded.subarray(0, origLen);
|
|
79
|
-
if (final.length !== data.length)
|
|
80
|
-
return false;
|
|
81
|
-
for (let i = 0; i < data.length; i++) {
|
|
82
|
-
if (final[i] !== data[i])
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
logger_1.logger.warn(`[GRG Inverse] Verification failed: ${e}`);
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
exports.GrgInverse = GrgInverse;
|
package/dist/grg_pipeline.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare class GrgPipeline {
|
|
2
|
-
static readonly MAX_INPUT_SIZE: number;
|
|
3
|
-
static processForward(data: Uint8Array, chainId: number, poolAddress: string): Uint8Array[];
|
|
4
|
-
static processInverse(shards: (Uint8Array | null)[], originalLength: number, chainId: number, poolAddress: string): Uint8Array;
|
|
5
|
-
}
|
package/dist/grg_pipeline.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GrgPipeline = void 0;
|
|
4
|
-
// sdk/src/grg_pipeline.ts
|
|
5
|
-
const grg_forward_1 = require("./grg_forward");
|
|
6
|
-
const grg_inverse_1 = require("./grg_inverse");
|
|
7
|
-
const logger_1 = require("./logger");
|
|
8
|
-
class GrgPipeline {
|
|
9
|
-
static MAX_INPUT_SIZE = 100 * 1024 * 1024; // 100 MB
|
|
10
|
-
static processForward(data, chainId, poolAddress) {
|
|
11
|
-
if (data.length > this.MAX_INPUT_SIZE) {
|
|
12
|
-
throw new Error(`[GRG] Input size ${data.length} exceeds MAX_INPUT_SIZE ${this.MAX_INPUT_SIZE}`);
|
|
13
|
-
}
|
|
14
|
-
logger_1.logger.info("Starting GRG forward pipeline...");
|
|
15
|
-
try {
|
|
16
|
-
const shards = grg_forward_1.GrgForward.encode(data, chainId, poolAddress);
|
|
17
|
-
logger_1.logger.info(`GRG forward pipeline complete. Generated ${shards.length} shards.`);
|
|
18
|
-
return shards;
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
logger_1.logger.error(`GRG forward pipeline failed: ${error}`);
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
static processInverse(shards, originalLength, chainId, poolAddress) {
|
|
26
|
-
logger_1.logger.info("Starting GRG inverse pipeline...");
|
|
27
|
-
try {
|
|
28
|
-
const hmacKey = grg_forward_1.GrgForward.deriveHmacKey(chainId, poolAddress);
|
|
29
|
-
const decodedShards = shards.map(s => {
|
|
30
|
-
try {
|
|
31
|
-
return s ? grg_inverse_1.GrgInverse.golayDecodeWrapper(s, hmacKey) : null;
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
logger_1.logger.warn(`Golay decode failed for a shard: ${e}`);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
const withLen = grg_inverse_1.GrgInverse.redstuffDecode(decodedShards);
|
|
39
|
-
const decodedLength = (withLen[0] << 24) | (withLen[1] << 16) | (withLen[2] << 8) | withLen[3];
|
|
40
|
-
const compressed = withLen.subarray(4);
|
|
41
|
-
const decompressed = grg_inverse_1.GrgInverse.golombDecode(compressed);
|
|
42
|
-
const final = decompressed.subarray(0, decodedLength);
|
|
43
|
-
if (final.length !== originalLength) {
|
|
44
|
-
throw new Error(`[GRG] Length mismatch in inverse: expected ${originalLength}, got ${final.length}`);
|
|
45
|
-
}
|
|
46
|
-
logger_1.logger.info("GRG inverse pipeline complete.");
|
|
47
|
-
return final;
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
logger_1.logger.error(`GRG inverse pipeline failed: ${error}`);
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
exports.GrgPipeline = GrgPipeline;
|
package/dist/reed_solomon.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class ReedSolomon {
|
|
2
|
-
private static expTable;
|
|
3
|
-
private static logTable;
|
|
4
|
-
private static initialized;
|
|
5
|
-
private static vandermondeCache;
|
|
6
|
-
static init(): void;
|
|
7
|
-
static mul(a: number, b: number): number;
|
|
8
|
-
static div(a: number, b: number): number;
|
|
9
|
-
static invertMatrix(matrix: number[][]): number[][];
|
|
10
|
-
static buildVandermonde(rows: number, cols: number): number[][];
|
|
11
|
-
static encode(data: Uint8Array, dataShards?: number, parityShards?: number): Uint8Array[];
|
|
12
|
-
static decode(shards: (Uint8Array | null)[], dataShards?: number, parityShards?: number): Uint8Array;
|
|
13
|
-
}
|
package/dist/reed_solomon.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// sdk/src/reed_solomon.ts
|
|
3
|
-
// Reed-Solomon GF(2^8) erasure coding
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.ReedSolomon = void 0;
|
|
6
|
-
class ReedSolomon {
|
|
7
|
-
static expTable = new Uint8Array(256);
|
|
8
|
-
static logTable = new Uint8Array(256);
|
|
9
|
-
static initialized = false;
|
|
10
|
-
static vandermondeCache = new Map();
|
|
11
|
-
static init() {
|
|
12
|
-
if (this.initialized)
|
|
13
|
-
return;
|
|
14
|
-
let x = 1;
|
|
15
|
-
for (let i = 0; i < 255; i++) {
|
|
16
|
-
this.expTable[i] = x;
|
|
17
|
-
this.logTable[x] = i;
|
|
18
|
-
x <<= 1;
|
|
19
|
-
if (x & 0x100) {
|
|
20
|
-
x ^= 0x11D; // x^8 + x^4 + x^3 + x^2 + 1
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
this.expTable[255] = this.expTable[0];
|
|
24
|
-
this.logTable[0] = 0;
|
|
25
|
-
this.initialized = true;
|
|
26
|
-
}
|
|
27
|
-
static mul(a, b) {
|
|
28
|
-
if (a === 0 || b === 0)
|
|
29
|
-
return 0;
|
|
30
|
-
return this.expTable[(this.logTable[a] + this.logTable[b]) % 255];
|
|
31
|
-
}
|
|
32
|
-
static div(a, b) {
|
|
33
|
-
if (b === 0)
|
|
34
|
-
throw new Error("Division by zero");
|
|
35
|
-
if (a === 0)
|
|
36
|
-
return 0;
|
|
37
|
-
return this.expTable[(this.logTable[a] - this.logTable[b] + 255) % 255];
|
|
38
|
-
}
|
|
39
|
-
static invertMatrix(matrix) {
|
|
40
|
-
const n = matrix.length;
|
|
41
|
-
const aug = [];
|
|
42
|
-
for (let i = 0; i < n; i++) {
|
|
43
|
-
aug[i] = [];
|
|
44
|
-
for (let j = 0; j < n; j++)
|
|
45
|
-
aug[i][j] = matrix[i][j];
|
|
46
|
-
for (let j = 0; j < n; j++)
|
|
47
|
-
aug[i][j + n] = (i === j) ? 1 : 0;
|
|
48
|
-
}
|
|
49
|
-
for (let i = 0; i < n; i++) {
|
|
50
|
-
let pivot = i;
|
|
51
|
-
while (pivot < n && aug[pivot][i] === 0)
|
|
52
|
-
pivot++;
|
|
53
|
-
if (pivot === n)
|
|
54
|
-
throw new Error("Singular matrix");
|
|
55
|
-
if (pivot !== i) {
|
|
56
|
-
const temp = aug[i];
|
|
57
|
-
aug[i] = aug[pivot];
|
|
58
|
-
aug[pivot] = temp;
|
|
59
|
-
}
|
|
60
|
-
const pivotVal = aug[i][i];
|
|
61
|
-
if (pivotVal !== 1) {
|
|
62
|
-
for (let j = i; j < 2 * n; j++)
|
|
63
|
-
aug[i][j] = this.div(aug[i][j], pivotVal);
|
|
64
|
-
}
|
|
65
|
-
for (let j = 0; j < n; j++) {
|
|
66
|
-
if (i !== j && aug[j][i] !== 0) {
|
|
67
|
-
const factor = aug[j][i];
|
|
68
|
-
for (let k = i; k < 2 * n; k++)
|
|
69
|
-
aug[j][k] ^= this.mul(factor, aug[i][k]);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const inv = [];
|
|
74
|
-
for (let i = 0; i < n; i++) {
|
|
75
|
-
inv[i] = [];
|
|
76
|
-
for (let j = 0; j < n; j++)
|
|
77
|
-
inv[i][j] = aug[i][j + n];
|
|
78
|
-
}
|
|
79
|
-
return inv;
|
|
80
|
-
}
|
|
81
|
-
static buildVandermonde(rows, cols) {
|
|
82
|
-
const cacheKey = `${rows}-${cols}`;
|
|
83
|
-
const cached = this.vandermondeCache.get(cacheKey);
|
|
84
|
-
if (cached)
|
|
85
|
-
return cached;
|
|
86
|
-
const V = [];
|
|
87
|
-
for (let r = 0; r < rows; r++) {
|
|
88
|
-
V[r] = [];
|
|
89
|
-
const x = r + 1;
|
|
90
|
-
for (let c = 0; c < cols; c++) {
|
|
91
|
-
V[r][c] = c === 0 ? 1 : this.mul(V[r][c - 1], x);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const V_top = [];
|
|
95
|
-
for (let i = 0; i < cols; i++)
|
|
96
|
-
V_top.push([...V[i]]);
|
|
97
|
-
const V_top_inv = this.invertMatrix(V_top);
|
|
98
|
-
const G = [];
|
|
99
|
-
for (let r = 0; r < rows; r++) {
|
|
100
|
-
G[r] = [];
|
|
101
|
-
for (let c = 0; c < cols; c++) {
|
|
102
|
-
let val = 0;
|
|
103
|
-
for (let k = 0; k < cols; k++)
|
|
104
|
-
val ^= this.mul(V[r][k], V_top_inv[k][c]);
|
|
105
|
-
G[r][c] = val;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
this.vandermondeCache.set(cacheKey, G);
|
|
109
|
-
return G;
|
|
110
|
-
}
|
|
111
|
-
static encode(data, dataShards = 4, parityShards = 2) {
|
|
112
|
-
this.init();
|
|
113
|
-
const totalShards = dataShards + parityShards;
|
|
114
|
-
const rawShardSize = Math.ceil(data.length / dataShards);
|
|
115
|
-
const shardSize = Math.ceil(rawShardSize / 3) * 3;
|
|
116
|
-
const matrix = this.buildVandermonde(totalShards, dataShards);
|
|
117
|
-
const shards = [];
|
|
118
|
-
for (let i = 0; i < totalShards; i++)
|
|
119
|
-
shards.push(new Uint8Array(shardSize));
|
|
120
|
-
for (let i = 0; i < dataShards; i++) {
|
|
121
|
-
shards[i].set(data.subarray(i * shardSize, Math.min((i + 1) * shardSize, data.length)));
|
|
122
|
-
}
|
|
123
|
-
for (let c = 0; c < shardSize; c++) {
|
|
124
|
-
for (let r = dataShards; r < totalShards; r++) {
|
|
125
|
-
let val = 0;
|
|
126
|
-
for (let j = 0; j < dataShards; j++)
|
|
127
|
-
val ^= this.mul(matrix[r][j], shards[j][c]);
|
|
128
|
-
shards[r][c] = val;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return shards;
|
|
132
|
-
}
|
|
133
|
-
static decode(shards, dataShards = 4, parityShards = 2) {
|
|
134
|
-
this.init();
|
|
135
|
-
const totalShards = dataShards + parityShards;
|
|
136
|
-
if (shards.length !== totalShards) {
|
|
137
|
-
throw new Error(`[RS] Expected ${totalShards} shards, got ${shards.length}`);
|
|
138
|
-
}
|
|
139
|
-
const presentIndices = [];
|
|
140
|
-
const presentShards = [];
|
|
141
|
-
for (let i = 0; i < totalShards; i++) {
|
|
142
|
-
if (shards[i] !== null && shards[i] !== undefined) {
|
|
143
|
-
presentIndices.push(i);
|
|
144
|
-
presentShards.push(shards[i]);
|
|
145
|
-
if (presentIndices.length === dataShards)
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (presentIndices.length < dataShards) {
|
|
150
|
-
throw new Error(`[RS] Not enough shards for recovery: need ${dataShards}, got ${presentIndices.length}`);
|
|
151
|
-
}
|
|
152
|
-
const shardSize = presentShards[0].length;
|
|
153
|
-
const origMatrix = this.buildVandermonde(totalShards, dataShards);
|
|
154
|
-
const subMatrix = [];
|
|
155
|
-
for (let i = 0; i < dataShards; i++)
|
|
156
|
-
subMatrix.push([...origMatrix[presentIndices[i]]]);
|
|
157
|
-
const invMatrix = this.invertMatrix(subMatrix);
|
|
158
|
-
const result = new Uint8Array(shardSize * dataShards);
|
|
159
|
-
for (let c = 0; c < shardSize; c++) {
|
|
160
|
-
for (let r = 0; r < dataShards; r++) {
|
|
161
|
-
let val = 0;
|
|
162
|
-
for (let j = 0; j < dataShards; j++)
|
|
163
|
-
val ^= this.mul(invMatrix[r][j], presentShards[j][c]);
|
|
164
|
-
result[r * shardSize + c] = val;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
exports.ReedSolomon = ReedSolomon;
|