@agirails/sdk 2.5.3 → 2.5.5

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 (169) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +72 -23
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +15 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +33 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +90 -12
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/config/networks.d.ts +10 -2
  20. package/dist/config/networks.d.ts.map +1 -1
  21. package/dist/config/networks.js +31 -22
  22. package/dist/config/networks.js.map +1 -1
  23. package/dist/level0/request.d.ts.map +1 -1
  24. package/dist/level0/request.js +2 -1
  25. package/dist/level0/request.js.map +1 -1
  26. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  27. package/dist/runtime/BlockchainRuntime.js +11 -5
  28. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  29. package/dist/utils/IPFSClient.d.ts +3 -1
  30. package/dist/utils/IPFSClient.d.ts.map +1 -1
  31. package/dist/utils/IPFSClient.js +27 -7
  32. package/dist/utils/IPFSClient.js.map +1 -1
  33. package/dist/wallet/AutoWalletProvider.d.ts +11 -1
  34. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  35. package/dist/wallet/AutoWalletProvider.js +84 -19
  36. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  37. package/dist/wallet/IWalletProvider.d.ts +34 -0
  38. package/dist/wallet/IWalletProvider.d.ts.map +1 -1
  39. package/dist/wallet/SmartWalletRouter.d.ts +128 -0
  40. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  41. package/dist/wallet/SmartWalletRouter.js +248 -0
  42. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  43. package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
  44. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  45. package/dist/wallet/aa/DualNonceManager.js +140 -6
  46. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  47. package/package.json +3 -6
  48. package/src/ACTPClient.ts +0 -1579
  49. package/src/abi/ACTPKernel.json +0 -1356
  50. package/src/abi/AgentRegistry.json +0 -915
  51. package/src/abi/ERC20.json +0 -40
  52. package/src/abi/EscrowVault.json +0 -134
  53. package/src/abi/IdentityRegistry.json +0 -316
  54. package/src/adapters/AdapterRegistry.ts +0 -173
  55. package/src/adapters/AdapterRouter.ts +0 -416
  56. package/src/adapters/BaseAdapter.ts +0 -498
  57. package/src/adapters/BasicAdapter.ts +0 -514
  58. package/src/adapters/IAdapter.ts +0 -292
  59. package/src/adapters/StandardAdapter.ts +0 -555
  60. package/src/adapters/X402Adapter.ts +0 -731
  61. package/src/adapters/index.ts +0 -60
  62. package/src/builders/DeliveryProofBuilder.ts +0 -327
  63. package/src/builders/QuoteBuilder.ts +0 -483
  64. package/src/builders/index.ts +0 -17
  65. package/src/cli/commands/balance.ts +0 -110
  66. package/src/cli/commands/batch.ts +0 -487
  67. package/src/cli/commands/config.ts +0 -231
  68. package/src/cli/commands/deploy-check.ts +0 -364
  69. package/src/cli/commands/deploy-env.ts +0 -120
  70. package/src/cli/commands/diff.ts +0 -141
  71. package/src/cli/commands/init.ts +0 -469
  72. package/src/cli/commands/mint.ts +0 -116
  73. package/src/cli/commands/pay.ts +0 -113
  74. package/src/cli/commands/publish.ts +0 -475
  75. package/src/cli/commands/pull.ts +0 -124
  76. package/src/cli/commands/register.ts +0 -247
  77. package/src/cli/commands/simulate.ts +0 -345
  78. package/src/cli/commands/time.ts +0 -302
  79. package/src/cli/commands/tx.ts +0 -448
  80. package/src/cli/commands/watch.ts +0 -211
  81. package/src/cli/index.ts +0 -134
  82. package/src/cli/utils/client.ts +0 -252
  83. package/src/cli/utils/config.ts +0 -389
  84. package/src/cli/utils/output.ts +0 -465
  85. package/src/cli/utils/wallet.ts +0 -109
  86. package/src/config/agirailsmd.ts +0 -262
  87. package/src/config/networks.ts +0 -275
  88. package/src/config/pendingPublish.ts +0 -237
  89. package/src/config/publishPipeline.ts +0 -359
  90. package/src/config/syncOperations.ts +0 -279
  91. package/src/erc8004/ERC8004Bridge.ts +0 -462
  92. package/src/erc8004/ReputationReporter.ts +0 -468
  93. package/src/erc8004/index.ts +0 -61
  94. package/src/errors/index.ts +0 -427
  95. package/src/index.ts +0 -364
  96. package/src/level0/Provider.ts +0 -117
  97. package/src/level0/ServiceDirectory.ts +0 -131
  98. package/src/level0/index.ts +0 -10
  99. package/src/level0/provide.ts +0 -132
  100. package/src/level0/request.ts +0 -432
  101. package/src/level1/Agent.ts +0 -1426
  102. package/src/level1/index.ts +0 -10
  103. package/src/level1/pricing/PriceCalculator.ts +0 -255
  104. package/src/level1/pricing/PricingStrategy.ts +0 -198
  105. package/src/level1/types/Job.ts +0 -179
  106. package/src/level1/types/Options.ts +0 -291
  107. package/src/level1/types/index.ts +0 -8
  108. package/src/protocol/ACTPKernel.ts +0 -808
  109. package/src/protocol/AgentRegistry.ts +0 -559
  110. package/src/protocol/DIDManager.ts +0 -629
  111. package/src/protocol/DIDResolver.ts +0 -554
  112. package/src/protocol/EASHelper.ts +0 -378
  113. package/src/protocol/EscrowVault.ts +0 -255
  114. package/src/protocol/EventMonitor.ts +0 -204
  115. package/src/protocol/MessageSigner.ts +0 -510
  116. package/src/protocol/ProofGenerator.ts +0 -339
  117. package/src/protocol/QuoteBuilder.ts +0 -15
  118. package/src/registry/AgentRegistryClient.ts +0 -202
  119. package/src/runtime/BlockchainRuntime.ts +0 -1015
  120. package/src/runtime/IACTPRuntime.ts +0 -306
  121. package/src/runtime/MockRuntime.ts +0 -1298
  122. package/src/runtime/MockStateManager.ts +0 -577
  123. package/src/runtime/index.ts +0 -25
  124. package/src/runtime/types/MockState.ts +0 -237
  125. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  126. package/src/storage/ArweaveClient.ts +0 -946
  127. package/src/storage/FilebaseClient.ts +0 -790
  128. package/src/storage/index.ts +0 -96
  129. package/src/storage/types.ts +0 -348
  130. package/src/types/adapter.ts +0 -310
  131. package/src/types/agent.ts +0 -79
  132. package/src/types/did.ts +0 -223
  133. package/src/types/eip712.ts +0 -175
  134. package/src/types/erc8004.ts +0 -293
  135. package/src/types/escrow.ts +0 -27
  136. package/src/types/index.ts +0 -17
  137. package/src/types/message.ts +0 -145
  138. package/src/types/state.ts +0 -87
  139. package/src/types/transaction.ts +0 -69
  140. package/src/types/x402.ts +0 -251
  141. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  142. package/src/utils/Helpers.ts +0 -688
  143. package/src/utils/IPFSClient.ts +0 -368
  144. package/src/utils/Logger.ts +0 -484
  145. package/src/utils/NonceManager.ts +0 -591
  146. package/src/utils/RateLimiter.ts +0 -534
  147. package/src/utils/ReceivedNonceTracker.ts +0 -567
  148. package/src/utils/SDKLifecycle.ts +0 -416
  149. package/src/utils/SecureNonce.ts +0 -78
  150. package/src/utils/Semaphore.ts +0 -276
  151. package/src/utils/UsedAttestationTracker.ts +0 -385
  152. package/src/utils/canonicalJson.ts +0 -38
  153. package/src/utils/circuitBreaker.ts +0 -324
  154. package/src/utils/computeTypeHash.ts +0 -48
  155. package/src/utils/fsSafe.ts +0 -80
  156. package/src/utils/index.ts +0 -80
  157. package/src/utils/retry.ts +0 -364
  158. package/src/utils/security.ts +0 -418
  159. package/src/utils/validation.ts +0 -540
  160. package/src/wallet/AutoWalletProvider.ts +0 -299
  161. package/src/wallet/EOAWalletProvider.ts +0 -69
  162. package/src/wallet/IWalletProvider.ts +0 -135
  163. package/src/wallet/aa/BundlerClient.ts +0 -274
  164. package/src/wallet/aa/DualNonceManager.ts +0 -173
  165. package/src/wallet/aa/PaymasterClient.ts +0 -174
  166. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  167. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  168. package/src/wallet/aa/constants.ts +0 -60
  169. package/src/wallet/keystore.ts +0 -240
@@ -1,567 +0,0 @@
1
- /**
2
- * ReceivedNonceTracker - Replay Attack Prevention for Message Receivers
3
- *
4
- * This utility tracks nonces of received messages to prevent replay attacks.
5
- * It works in conjunction with NonceManager (for senders) but serves the receiver side.
6
- *
7
- * Reference: V4 Security Vulnerability (EIP-712 Replay Attack)
8
- *
9
- * Usage Pattern:
10
- * - Sender: Uses NonceManager to generate monotonically increasing nonces
11
- * - Receiver: Uses ReceivedNonceTracker to validate and track received nonces
12
- *
13
- * Security Properties:
14
- * 1. Nonces must be monotonically increasing per sender + message type
15
- * 2. Duplicate nonces are rejected (replay attack prevention)
16
- * 3. Nonces that are lower than the highest seen are rejected (old replay prevention)
17
- *
18
- * ⚠️ WARNING: In-memory tracking only. For production:
19
- * - Use persistent storage (Redis, PostgreSQL, etc.)
20
- * - Implement nonce recovery from transaction history
21
- * - Consider nonce expiry for long-running processes
22
- */
23
-
24
- /**
25
- * Nonce validation result
26
- */
27
- export interface NonceValidationResult {
28
- valid: boolean;
29
- reason?: string;
30
- expectedMinimum?: string; // bytes32 format
31
- receivedNonce?: string; // bytes32 format
32
- }
33
-
34
- /**
35
- * Interface for tracking received nonces
36
- */
37
- export interface IReceivedNonceTracker {
38
- /**
39
- * Validate and record a received nonce
40
- * @param sender - Sender DID (e.g., "did:ethr:0x...")
41
- * @param messageType - Message type (e.g., "agirails.delivery.v1")
42
- * @param nonce - Nonce value (bytes32 format: "0x...")
43
- * @returns Validation result
44
- */
45
- validateAndRecord(sender: string, messageType: string, nonce: string): NonceValidationResult;
46
-
47
- /**
48
- * Check if a nonce has been used (without recording)
49
- * @param sender - Sender DID
50
- * @param messageType - Message type
51
- * @param nonce - Nonce value (bytes32 format)
52
- * @returns true if nonce was already used
53
- */
54
- hasBeenUsed(sender: string, messageType: string, nonce: string): boolean;
55
-
56
- /**
57
- * Get highest nonce seen for sender + message type
58
- * @param sender - Sender DID
59
- * @param messageType - Message type
60
- * @returns Highest nonce (bytes32 format) or null if none seen
61
- */
62
- getHighestNonce(sender: string, messageType: string): string | null;
63
-
64
- /**
65
- * Reset tracking for a specific sender + message type
66
- * @param sender - Sender DID
67
- * @param messageType - Message type
68
- */
69
- reset(sender: string, messageType: string): void;
70
-
71
- /**
72
- * Clear all tracked nonces
73
- */
74
- clearAll(): void;
75
- }
76
-
77
- /**
78
- * In-Memory Received Nonce Tracker
79
- *
80
- * Strategy: Track highest nonce seen per sender + message type
81
- * - Accept nonces that are strictly greater than the highest seen
82
- * - Reject nonces that are <= highest seen (replay attack)
83
- *
84
- * Trade-off:
85
- * - Memory efficient (one value per sender + type)
86
- * - Requires ordered nonce sequences
87
- * - Cannot skip nonces (nonce gaps are rejected)
88
- */
89
- export class InMemoryReceivedNonceTracker implements IReceivedNonceTracker {
90
- // Map: sender -> messageType -> highest nonce (as BigInt for comparison)
91
- private highestNonces: Map<string, Map<string, bigint>> = new Map();
92
-
93
- /**
94
- * Validate and record a received nonce
95
- */
96
- validateAndRecord(sender: string, messageType: string, nonce: string): NonceValidationResult {
97
- // Validate nonce format (must be bytes32: 0x + 64 hex chars)
98
- if (!/^0x[a-fA-F0-9]{64}$/.test(nonce)) {
99
- return {
100
- valid: false,
101
- reason: 'Invalid nonce format (must be bytes32)',
102
- receivedNonce: nonce
103
- };
104
- }
105
-
106
- // Convert nonce to BigInt for comparison
107
- const nonceValue = BigInt(nonce);
108
-
109
- // Get sender's nonce map
110
- let senderNonces = this.highestNonces.get(sender);
111
- if (!senderNonces) {
112
- senderNonces = new Map<string, bigint>();
113
- this.highestNonces.set(sender, senderNonces);
114
- }
115
-
116
- // Get highest nonce for this message type
117
- const highestNonce = senderNonces.get(messageType);
118
-
119
- if (highestNonce === undefined) {
120
- // First message from this sender for this type
121
- senderNonces.set(messageType, nonceValue);
122
- return { valid: true };
123
- }
124
-
125
- // Nonce must be strictly greater than highest seen
126
- if (nonceValue <= highestNonce) {
127
- const expectedMinimum = '0x' + (highestNonce + BigInt(1)).toString(16).padStart(64, '0');
128
- return {
129
- valid: false,
130
- reason: `Nonce replay detected: nonce must be > ${this.bigintToBytes32(highestNonce)}`,
131
- expectedMinimum,
132
- receivedNonce: nonce
133
- };
134
- }
135
-
136
- // Valid nonce - record it
137
- senderNonces.set(messageType, nonceValue);
138
- return { valid: true };
139
- }
140
-
141
- /**
142
- * Check if a nonce has been used (non-mutating)
143
- */
144
- hasBeenUsed(sender: string, messageType: string, nonce: string): boolean {
145
- const nonceValue = BigInt(nonce);
146
- const senderNonces = this.highestNonces.get(sender);
147
-
148
- if (!senderNonces) {
149
- return false; // No nonces seen from this sender
150
- }
151
-
152
- const highestNonce = senderNonces.get(messageType);
153
- if (highestNonce === undefined) {
154
- return false; // No nonces seen for this message type
155
- }
156
-
157
- // If the provided nonce is <= highest seen, it's been "used"
158
- return nonceValue <= highestNonce;
159
- }
160
-
161
- /**
162
- * Get highest nonce seen
163
- */
164
- getHighestNonce(sender: string, messageType: string): string | null {
165
- const senderNonces = this.highestNonces.get(sender);
166
- if (!senderNonces) {
167
- return null;
168
- }
169
-
170
- const highestNonce = senderNonces.get(messageType);
171
- if (highestNonce === undefined) {
172
- return null;
173
- }
174
-
175
- return this.bigintToBytes32(highestNonce);
176
- }
177
-
178
- /**
179
- * Reset tracking for sender + message type
180
- */
181
- reset(sender: string, messageType: string): void {
182
- const senderNonces = this.highestNonces.get(sender);
183
- if (senderNonces) {
184
- senderNonces.delete(messageType);
185
- // Clean up sender map if empty
186
- if (senderNonces.size === 0) {
187
- this.highestNonces.delete(sender);
188
- }
189
- }
190
- }
191
-
192
- /**
193
- * Clear all tracked nonces
194
- */
195
- clearAll(): void {
196
- this.highestNonces.clear();
197
- }
198
-
199
- /**
200
- * Convert BigInt to bytes32 hex string
201
- */
202
- private bigintToBytes32(value: bigint): string {
203
- return '0x' + value.toString(16).padStart(64, '0');
204
- }
205
-
206
- /**
207
- * Get all nonces (for debugging/persistence)
208
- */
209
- getAllNonces(): Record<string, Record<string, string>> {
210
- const result: Record<string, Record<string, string>> = {};
211
-
212
- this.highestNonces.forEach((senderNonces, sender) => {
213
- result[sender] = {};
214
- senderNonces.forEach((nonce, messageType) => {
215
- result[sender][messageType] = this.bigintToBytes32(nonce);
216
- });
217
- });
218
-
219
- return result;
220
- }
221
- }
222
-
223
- /**
224
- * Set-Based Received Nonce Tracker
225
- *
226
- * Strategy: Track exact set of used nonces per sender + message type
227
- * - Accept nonces that haven't been seen before
228
- * - Reject duplicate nonces (replay attack)
229
- * - Allows non-sequential nonces (nonce gaps are OK)
230
- *
231
- * SECURITY FIX (NEW-H-2): Max size enforcement to prevent memory exhaustion
232
- * SECURITY FIX (HIGH-2): Global total entries limit to prevent DoS via many sender combinations
233
- * SECURITY FIX (H-2): Rate limiting per sender to prevent flood attacks
234
- *
235
- * Trade-off:
236
- * - Higher memory usage (stores every nonce)
237
- * - More flexible (allows out-of-order delivery)
238
- * - Requires periodic cleanup to prevent unbounded growth
239
- */
240
- export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
241
- // Map: sender -> messageType -> Set of used nonces
242
- private usedNonces: Map<string, Map<string, Set<string>>> = new Map();
243
-
244
- // SECURITY FIX (NEW-H-2): Maximum entries per sender+messageType
245
- private readonly maxSizePerType: number;
246
-
247
- // SECURITY FIX (HIGH-2): Global limit across ALL sender+messageType combinations
248
- private readonly maxTotalEntries: number;
249
- private totalEntries: number = 0;
250
-
251
- // SECURITY FIX (H-2): Rate limiting per sender
252
- // Map: sender -> { count: number, windowStart: number }
253
- private rateLimitState: Map<string, { count: number; windowStart: number }> = new Map();
254
- private readonly maxNoncesPerMinute: number;
255
- private readonly rateLimitWindowMs: number = 60000; // 1 minute window
256
-
257
- /**
258
- * Create set-based tracker with optional max size
259
- * @param maxSizePerType - Maximum nonces per sender+messageType (default: 10,000)
260
- * @param maxTotalEntries - Maximum total nonces across all combinations (default: 100,000)
261
- * @param maxNoncesPerMinute - Maximum nonces per sender per minute (default: 100)
262
- */
263
- constructor(
264
- maxSizePerType: number = 10000,
265
- maxTotalEntries: number = 100000,
266
- maxNoncesPerMinute: number = 100
267
- ) {
268
- if (maxSizePerType <= 0) {
269
- throw new Error('maxSizePerType must be positive');
270
- }
271
- if (maxTotalEntries <= 0) {
272
- throw new Error('maxTotalEntries must be positive');
273
- }
274
- if (maxNoncesPerMinute <= 0) {
275
- throw new Error('maxNoncesPerMinute must be positive');
276
- }
277
- this.maxSizePerType = maxSizePerType;
278
- this.maxTotalEntries = maxTotalEntries;
279
- this.maxNoncesPerMinute = maxNoncesPerMinute;
280
- }
281
-
282
- /**
283
- * SECURITY FIX (H-2): Check rate limit for sender
284
- * @param sender - Sender DID
285
- * @returns true if rate limit exceeded
286
- */
287
- private checkRateLimit(sender: string): boolean {
288
- const now = Date.now();
289
- const state = this.rateLimitState.get(sender);
290
-
291
- if (!state) {
292
- // First nonce from this sender
293
- this.rateLimitState.set(sender, { count: 1, windowStart: now });
294
- return false; // Not rate limited
295
- }
296
-
297
- // Check if window expired (reset counter)
298
- if (now - state.windowStart >= this.rateLimitWindowMs) {
299
- state.count = 1;
300
- state.windowStart = now;
301
- return false; // Not rate limited
302
- }
303
-
304
- // Increment counter
305
- state.count++;
306
-
307
- // Check if rate limit exceeded
308
- return state.count > this.maxNoncesPerMinute;
309
- }
310
-
311
- /**
312
- * SECURITY FIX (H-2): Periodic cleanup of rate limit state
313
- * Removes expired rate limit entries (older than 5 minutes)
314
- */
315
- private cleanupRateLimitState(): void {
316
- const now = Date.now();
317
- const expiryThreshold = 5 * 60 * 1000; // 5 minutes
318
-
319
- for (const [sender, state] of this.rateLimitState.entries()) {
320
- if (now - state.windowStart > expiryThreshold) {
321
- this.rateLimitState.delete(sender);
322
- }
323
- }
324
- }
325
-
326
- /**
327
- * Validate and record a received nonce
328
- *
329
- * SECURITY FIX (NEW-H-2): Automatic cleanup when max size reached
330
- * SECURITY FIX (HIGH-2): Global limit check to prevent DoS
331
- * SECURITY FIX (H-2): Rate limiting per sender (max 100 nonces/minute)
332
- */
333
- validateAndRecord(sender: string, messageType: string, nonce: string): NonceValidationResult {
334
- // Validate nonce format
335
- if (!/^0x[a-fA-F0-9]{64}$/.test(nonce)) {
336
- return {
337
- valid: false,
338
- reason: 'Invalid nonce format (must be bytes32)',
339
- receivedNonce: nonce
340
- };
341
- }
342
-
343
- // SECURITY FIX (H-2): Rate limit check (BEFORE global limit to avoid unnecessary work)
344
- if (this.checkRateLimit(sender)) {
345
- return {
346
- valid: false,
347
- reason: `Rate limit exceeded for sender ${sender}: ` +
348
- `Maximum ${this.maxNoncesPerMinute} nonces per minute allowed. ` +
349
- `This may indicate a flood attack or misconfigured client. ` +
350
- `Wait 1 minute before retrying.`,
351
- receivedNonce: nonce
352
- };
353
- }
354
-
355
- // SECURITY FIX (H-2): Periodic cleanup every 100 validations (amortized cost)
356
- if (this.totalEntries % 100 === 0) {
357
- this.cleanupRateLimitState();
358
- }
359
-
360
- // SECURITY FIX (HIGH-2): Check global limit BEFORE adding
361
- if (this.totalEntries >= this.maxTotalEntries) {
362
- return {
363
- valid: false,
364
- reason: `Global nonce tracker limit reached (${this.maxTotalEntries} entries). ` +
365
- `This may indicate a DoS attack or need for cleanup. ` +
366
- `Current usage: ${this.totalEntries} entries across ${this.getCombinationCount()} sender+type combinations.`,
367
- receivedNonce: nonce
368
- };
369
- }
370
-
371
- // Get sender's nonce map
372
- let senderNonces = this.usedNonces.get(sender);
373
- if (!senderNonces) {
374
- senderNonces = new Map<string, Set<string>>();
375
- this.usedNonces.set(sender, senderNonces);
376
- }
377
-
378
- // Get set of used nonces for this message type
379
- let usedSet = senderNonces.get(messageType);
380
- if (!usedSet) {
381
- usedSet = new Set<string>();
382
- senderNonces.set(messageType, usedSet);
383
- }
384
-
385
- // Check if nonce was already used
386
- if (usedSet.has(nonce)) {
387
- return {
388
- valid: false,
389
- reason: 'Nonce replay detected: this nonce has already been used',
390
- receivedNonce: nonce
391
- };
392
- }
393
-
394
- // SECURITY FIX (NEW-H-2): Auto-cleanup if max size per type reached
395
- if (usedSet.size >= this.maxSizePerType) {
396
- // Keep only last 80% of entries (sorted by nonce value)
397
- const keepCount = Math.floor(this.maxSizePerType * 0.8);
398
- const sortedNonces = Array.from(usedSet).sort((a, b) => {
399
- const aVal = BigInt(a);
400
- const bVal = BigInt(b);
401
- return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
402
- });
403
- const removedCount = usedSet.size - keepCount;
404
- usedSet = new Set(sortedNonces.slice(-keepCount));
405
- senderNonces.set(messageType, usedSet);
406
- // SECURITY FIX (HIGH-2): Update global counter
407
- this.totalEntries -= removedCount;
408
- }
409
-
410
- // Valid nonce - record it
411
- usedSet.add(nonce);
412
- // SECURITY FIX (HIGH-2): Update global counter
413
- this.totalEntries++;
414
- return { valid: true };
415
- }
416
-
417
- /**
418
- * Get number of sender+messageType combinations (for monitoring)
419
- * SECURITY FIX (HIGH-2): Monitoring method
420
- */
421
- private getCombinationCount(): number {
422
- let count = 0;
423
- this.usedNonces.forEach(senderMap => {
424
- count += senderMap.size;
425
- });
426
- return count;
427
- }
428
-
429
- /**
430
- * Get memory usage statistics
431
- * SECURITY FIX (HIGH-2): Monitoring method for DoS detection
432
- */
433
- getMemoryUsage(): { totalEntries: number; combinations: number; maxTotalEntries: number } {
434
- return {
435
- totalEntries: this.totalEntries,
436
- combinations: this.getCombinationCount(),
437
- maxTotalEntries: this.maxTotalEntries
438
- };
439
- }
440
-
441
- /**
442
- * Check if a nonce has been used
443
- */
444
- hasBeenUsed(sender: string, messageType: string, nonce: string): boolean {
445
- const senderNonces = this.usedNonces.get(sender);
446
- if (!senderNonces) {
447
- return false;
448
- }
449
-
450
- const usedSet = senderNonces.get(messageType);
451
- if (!usedSet) {
452
- return false;
453
- }
454
-
455
- return usedSet.has(nonce);
456
- }
457
-
458
- /**
459
- * Get highest nonce seen (for this strategy, compute from set)
460
- */
461
- getHighestNonce(sender: string, messageType: string): string | null {
462
- const senderNonces = this.usedNonces.get(sender);
463
- if (!senderNonces) {
464
- return null;
465
- }
466
-
467
- const usedSet = senderNonces.get(messageType);
468
- if (!usedSet || usedSet.size === 0) {
469
- return null;
470
- }
471
-
472
- // Find maximum nonce in set
473
- let maxNonce = BigInt(0);
474
- usedSet.forEach(nonce => {
475
- const value = BigInt(nonce);
476
- if (value > maxNonce) {
477
- maxNonce = value;
478
- }
479
- });
480
-
481
- return '0x' + maxNonce.toString(16).padStart(64, '0');
482
- }
483
-
484
- /**
485
- * Reset tracking for sender + message type
486
- */
487
- reset(sender: string, messageType: string): void {
488
- const senderNonces = this.usedNonces.get(sender);
489
- if (senderNonces) {
490
- const usedSet = senderNonces.get(messageType);
491
- if (usedSet) {
492
- // SECURITY FIX (HIGH-2): Update global counter
493
- this.totalEntries -= usedSet.size;
494
- }
495
- senderNonces.delete(messageType);
496
- if (senderNonces.size === 0) {
497
- this.usedNonces.delete(sender);
498
- }
499
- }
500
- }
501
-
502
- /**
503
- * Clear all tracked nonces
504
- */
505
- clearAll(): void {
506
- this.usedNonces.clear();
507
- // SECURITY FIX (HIGH-2): Reset global counter
508
- this.totalEntries = 0;
509
- }
510
-
511
- /**
512
- * Get nonce count for sender + message type (for monitoring)
513
- */
514
- getNonceCount(sender: string, messageType: string): number {
515
- const senderNonces = this.usedNonces.get(sender);
516
- if (!senderNonces) {
517
- return 0;
518
- }
519
-
520
- const usedSet = senderNonces.get(messageType);
521
- return usedSet ? usedSet.size : 0;
522
- }
523
-
524
- /**
525
- * Cleanup old nonces (keep only last N)
526
- * This prevents unbounded memory growth
527
- */
528
- cleanup(sender: string, messageType: string, keepLast: number = 1000): void {
529
- const senderNonces = this.usedNonces.get(sender);
530
- if (!senderNonces) {
531
- return;
532
- }
533
-
534
- const usedSet = senderNonces.get(messageType);
535
- if (!usedSet || usedSet.size <= keepLast) {
536
- return;
537
- }
538
-
539
- // Convert to array and sort by nonce value
540
- const sortedNonces = Array.from(usedSet).sort((a, b) => {
541
- const aVal = BigInt(a);
542
- const bVal = BigInt(b);
543
- return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
544
- });
545
-
546
- // Keep only the last N nonces
547
- const removedCount = usedSet.size - keepLast;
548
- const toKeep = new Set(sortedNonces.slice(-keepLast));
549
- senderNonces.set(messageType, toKeep);
550
- // SECURITY FIX (HIGH-2): Update global counter
551
- this.totalEntries -= removedCount;
552
- }
553
- }
554
-
555
- /**
556
- * Factory function to create a nonce tracker
557
- * @param strategy - 'memory-efficient' (highest nonce) or 'set-based' (all nonces)
558
- * @returns IReceivedNonceTracker instance
559
- */
560
- export function createReceivedNonceTracker(
561
- strategy: 'memory-efficient' | 'set-based' = 'memory-efficient'
562
- ): IReceivedNonceTracker {
563
- if (strategy === 'set-based') {
564
- return new SetBasedReceivedNonceTracker();
565
- }
566
- return new InMemoryReceivedNonceTracker();
567
- }