@wishknish/knishio-client-ts 0.7.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 (141) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +425 -0
  3. package/dist/index.cjs +9961 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.iife.js +32241 -0
  6. package/dist/index.iife.js.map +1 -0
  7. package/dist/index.js +9853 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +113 -0
  10. package/src/AuthToken.ts +214 -0
  11. package/src/KnishIOClient.ts +2020 -0
  12. package/src/constants.ts +398 -0
  13. package/src/core/Atom.ts +646 -0
  14. package/src/core/AtomMeta.ts +278 -0
  15. package/src/core/Meta.ts +428 -0
  16. package/src/core/Molecule.ts +825 -0
  17. package/src/core/PolicyMeta.ts +130 -0
  18. package/src/core/TokenUnit.ts +148 -0
  19. package/src/core/Wallet.ts +467 -0
  20. package/src/exception/AtomIndexException.ts +97 -0
  21. package/src/exception/AtomsMissingException.ts +109 -0
  22. package/src/exception/AuthorizationRejectedException.ts +63 -0
  23. package/src/exception/BalanceInsufficientException.ts +58 -0
  24. package/src/exception/BaseException.ts +275 -0
  25. package/src/exception/BatchIdException.ts +58 -0
  26. package/src/exception/CodeException.ts +119 -0
  27. package/src/exception/DecryptionKeyException.ts +65 -0
  28. package/src/exception/InvalidResponseException.ts +112 -0
  29. package/src/exception/MetaMissingException.ts +58 -0
  30. package/src/exception/MolecularHashMismatchException.ts +115 -0
  31. package/src/exception/MolecularHashMissingException.ts +58 -0
  32. package/src/exception/NegativeAmountException.ts +58 -0
  33. package/src/exception/PolicyInvalidException.ts +58 -0
  34. package/src/exception/SignatureMalformedException.ts +58 -0
  35. package/src/exception/SignatureMismatchException.ts +98 -0
  36. package/src/exception/StackableUnitAmountException.ts +65 -0
  37. package/src/exception/StackableUnitDecimalsException.ts +65 -0
  38. package/src/exception/TransferBalanceException.ts +98 -0
  39. package/src/exception/TransferMalformedException.ts +58 -0
  40. package/src/exception/TransferMismatchedException.ts +58 -0
  41. package/src/exception/TransferRemainderException.ts +58 -0
  42. package/src/exception/TransferToSelfException.ts +58 -0
  43. package/src/exception/TransferUnbalancedException.ts +58 -0
  44. package/src/exception/UnauthenticatedException.ts +147 -0
  45. package/src/exception/WalletCredentialException.ts +108 -0
  46. package/src/exception/WalletShadowException.ts +65 -0
  47. package/src/exception/WrongTokenTypeException.ts +58 -0
  48. package/src/exception/index.ts +272 -0
  49. package/src/index.ts +512 -0
  50. package/src/instance/rules/Callback.ts +257 -0
  51. package/src/instance/rules/Condition.ts +96 -0
  52. package/src/instance/rules/Meta.ts +163 -0
  53. package/src/instance/rules/Rule.ts +29 -0
  54. package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
  55. package/src/libraries/CheckMolecule.ts +581 -0
  56. package/src/libraries/Decimal.ts +94 -0
  57. package/src/libraries/Dot.ts +202 -0
  58. package/src/libraries/GraphQLClient.ts +276 -0
  59. package/src/libraries/Hex.ts +155 -0
  60. package/src/libraries/UrqlClientWrapper.ts +336 -0
  61. package/src/libraries/array.ts +91 -0
  62. package/src/libraries/crypto.ts +816 -0
  63. package/src/libraries/strings.ts +458 -0
  64. package/src/mutation/Mutation.ts +103 -0
  65. package/src/mutation/MutationActiveSession.ts +108 -0
  66. package/src/mutation/MutationClaimShadowWallet.ts +91 -0
  67. package/src/mutation/MutationCreateIdentifier.ts +86 -0
  68. package/src/mutation/MutationCreateMeta.ts +92 -0
  69. package/src/mutation/MutationCreateRule.ts +90 -0
  70. package/src/mutation/MutationCreateToken.ts +92 -0
  71. package/src/mutation/MutationCreateWallet.ts +78 -0
  72. package/src/mutation/MutationDepositBufferToken.ts +72 -0
  73. package/src/mutation/MutationLinkIdentifier.ts +87 -0
  74. package/src/mutation/MutationProposeMolecule.ts +147 -0
  75. package/src/mutation/MutationRequestAuthorization.ts +77 -0
  76. package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
  77. package/src/mutation/MutationRequestTokens.ts +98 -0
  78. package/src/mutation/MutationTransferTokens.ts +87 -0
  79. package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
  80. package/src/query/Query.ts +187 -0
  81. package/src/query/QueryActiveSession.ts +89 -0
  82. package/src/query/QueryAtom.ts +275 -0
  83. package/src/query/QueryBalance.ts +102 -0
  84. package/src/query/QueryBatch.ts +145 -0
  85. package/src/query/QueryBatchHistory.ts +86 -0
  86. package/src/query/QueryContinuId.ts +91 -0
  87. package/src/query/QueryMetaType.ts +177 -0
  88. package/src/query/QueryMetaTypeViaAtom.ts +199 -0
  89. package/src/query/QueryPolicy.ts +91 -0
  90. package/src/query/QueryToken.ts +90 -0
  91. package/src/query/QueryUserActivity.ts +153 -0
  92. package/src/query/QueryWalletBundle.ts +92 -0
  93. package/src/query/QueryWalletList.ts +106 -0
  94. package/src/response/EnhancedResponse.ts +345 -0
  95. package/src/response/Response.ts +254 -0
  96. package/src/response/ResponseActiveSession.ts +72 -0
  97. package/src/response/ResponseAtom.ts +132 -0
  98. package/src/response/ResponseAuthorizationGuest.ts +119 -0
  99. package/src/response/ResponseBalance.ts +153 -0
  100. package/src/response/ResponseClaimShadowWallet.ts +56 -0
  101. package/src/response/ResponseContinuId.ts +101 -0
  102. package/src/response/ResponseCreateIdentifier.ts +56 -0
  103. package/src/response/ResponseCreateMeta.ts +58 -0
  104. package/src/response/ResponseCreateRule.ts +56 -0
  105. package/src/response/ResponseCreateToken.ts +58 -0
  106. package/src/response/ResponseCreateWallet.ts +58 -0
  107. package/src/response/ResponseLinkIdentifier.ts +87 -0
  108. package/src/response/ResponseMetaBatch.ts +72 -0
  109. package/src/response/ResponseMetaType.ts +108 -0
  110. package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
  111. package/src/response/ResponsePolicy.ts +90 -0
  112. package/src/response/ResponseProposeMolecule.ts +155 -0
  113. package/src/response/ResponseQueryActiveSession.ts +105 -0
  114. package/src/response/ResponseQueryUserActivity.ts +89 -0
  115. package/src/response/ResponseRequestAuthorization.ts +100 -0
  116. package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
  117. package/src/response/ResponseRequestTokens.ts +58 -0
  118. package/src/response/ResponseTransferTokens.ts +72 -0
  119. package/src/response/ResponseWalletBundle.ts +95 -0
  120. package/src/response/ResponseWalletList.ts +165 -0
  121. package/src/schemas/index.ts +457 -0
  122. package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
  123. package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
  124. package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
  125. package/src/subscribe/Subscribe.ts +182 -0
  126. package/src/subscribe/WalletStatusSubscribe.ts +70 -0
  127. package/src/subscribe/index.ts +59 -0
  128. package/src/types/assertions.ts +722 -0
  129. package/src/types/client.ts +567 -0
  130. package/src/types/crypto.ts +541 -0
  131. package/src/types/graphql.ts +630 -0
  132. package/src/types/guards.ts +659 -0
  133. package/src/types/index.ts +614 -0
  134. package/src/types/response.ts +133 -0
  135. package/src/types/template-literals.ts +382 -0
  136. package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
  137. package/src/validation/ValidationService.ts +607 -0
  138. package/src/validation/schemas.ts +447 -0
  139. package/src/versions/HashAtom.ts +170 -0
  140. package/src/versions/Version4.ts +120 -0
  141. package/src/versions/index.ts +80 -0
@@ -0,0 +1,816 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ /**
50
+ * Cryptographic library for KnishIO TypeScript SDK
51
+ * Ensures 100% compatibility with JavaScript SDK and cross-platform consistency
52
+ */
53
+
54
+ import JsSHA from 'jssha'
55
+ import { randomString } from './strings'
56
+ import type {
57
+ BundleHash,
58
+ Position,
59
+ SHAKE256Options,
60
+ SecretGenerationOptions,
61
+ BundleHashOptions,
62
+ PositionGenerationOptions
63
+ } from '@/types'
64
+
65
+ // =============================================================================
66
+ // CONSTANTS - MUST MATCH JavaScript SDK EXACTLY
67
+ // =============================================================================
68
+
69
+ export const CRYPTO_CONSTANTS = {
70
+ SECRET_LENGTH: 2048,
71
+ BUNDLE_HASH_LENGTH: 64,
72
+ POSITION_LENGTH: 64,
73
+ ADDRESS_LENGTH: 64,
74
+ MOLECULAR_HASH_OUTPUT_LENGTH: 256,
75
+ SHAKE256_OUTPUT_LENGTH: 256,
76
+ KEY_FRAGMENT_SIZE: 128,
77
+ HASH_ITERATIONS: 16
78
+ } as const
79
+
80
+ // =============================================================================
81
+ // SHAKE256 IMPLEMENTATION - CRITICAL FOR CROSS-SDK COMPATIBILITY
82
+ // =============================================================================
83
+
84
+ /**
85
+ * SHAKE256 hash function - MUST produce identical outputs to JavaScript SDK
86
+ * Test vectors from validation/common-config.json:
87
+ * - shake256("test", 256) === "b54ff7255705a71ee2925e4a3e30e41aed489a579d5595e0df13e32e1e4dd202"
88
+ * - shake256("KnishIO", 256) === "35e3c3f33aefb940baaf430855ccb441c24b7b0542f682b8543f4c9d3a077c6e"
89
+ *
90
+ * @param input - Input string to hash
91
+ * @param outputLength - Output length in BITS (256 bits = 64 hex chars)
92
+ */
93
+ export function shake256(input: string, outputLength: number): string {
94
+ const sponge = new JsSHA('SHAKE256', 'TEXT')
95
+ sponge.update(input)
96
+
97
+ // CRITICAL FIX: outputLength parameter is in BITS (as per Implementation Guide)
98
+ // JsSHA outputLen expects bits, so pass outputLength directly
99
+ // 256 bits = 64 hex characters, 512 bits = 128 hex characters, etc.
100
+ const result = sponge.getHash('HEX', { outputLen: outputLength })
101
+
102
+ return result.toLowerCase()
103
+ }
104
+
105
+ /**
106
+ * Enhanced SHAKE256 with options
107
+ * @param options.input - Input string or Uint8Array to hash
108
+ * @param options.outputLength - Output length in BITS
109
+ * @param options.encoding - Output encoding format
110
+ */
111
+ export function shake256Enhanced(options: SHAKE256Options): string {
112
+ const { input, outputLength, encoding = 'hex' } = options
113
+
114
+ const sponge = new JsSHA('SHAKE256', typeof input === 'string' ? 'TEXT' : 'UINT8ARRAY')
115
+ sponge.update(input)
116
+
117
+ // outputLength is in BITS consistently across all encodings
118
+ switch (encoding) {
119
+ case 'hex':
120
+ return sponge.getHash('HEX', { outputLen: outputLength }).toLowerCase()
121
+ case 'base64':
122
+ return sponge.getHash('B64', { outputLen: outputLength })
123
+ case 'bytes':
124
+ return sponge.getHash('UINT8ARRAY', { outputLen: outputLength }).toString()
125
+ default:
126
+ throw new Error(`Unsupported encoding: ${encoding}`)
127
+ }
128
+ }
129
+
130
+ // =============================================================================
131
+ // SECRET GENERATION - MUST MATCH JavaScript SDK BEHAVIOR
132
+ // =============================================================================
133
+
134
+ /**
135
+ * Generate secret from seed or random - MUST match JS SDK exactly
136
+ * If seed provided: uses SHAKE256 to generate deterministic secret
137
+ * If no seed: uses cryptographically secure random string
138
+ */
139
+ export function generateSecret(seed: string | null = null, length = CRYPTO_CONSTANTS.SECRET_LENGTH): string {
140
+ if (seed) {
141
+ // Deterministic generation using SHAKE256 - MUST match JS SDK
142
+ const sponge = new JsSHA('SHAKE256', 'TEXT')
143
+ sponge.update(seed)
144
+ // Fix: outputLen is in BITS, so for 1024 hex chars output, we need length*2 bits (matching JS SDK)
145
+ return sponge.getHash('HEX', { outputLen: length * 2 }).toLowerCase()
146
+ } else {
147
+ // Cryptographically secure random generation
148
+ return randomString(length)
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Enhanced secret generation with options
154
+ */
155
+ export function generateSecretEnhanced(options: SecretGenerationOptions): string {
156
+ const {
157
+ seed = null,
158
+ length = CRYPTO_CONSTANTS.SECRET_LENGTH,
159
+ algorithm = 'SHAKE256'
160
+ } = options
161
+
162
+ if (algorithm !== 'SHAKE256') {
163
+ throw new Error('Only SHAKE256 algorithm is supported for cross-platform compatibility')
164
+ }
165
+
166
+ return generateSecret(seed, length)
167
+ }
168
+
169
+ // =============================================================================
170
+ // BUNDLE HASH GENERATION - MUST MATCH JavaScript SDK BEHAVIOR
171
+ // =============================================================================
172
+
173
+ /**
174
+ * Generate bundle hash from secret - MUST produce identical results to JS SDK
175
+ * Uses SHAKE256 with 256-bit output (64 hex characters)
176
+ */
177
+ export function generateBundleHash(secret: string, source: string | null = null): BundleHash {
178
+ if (!secret || secret.length === 0) {
179
+ throw new Error('Secret is required for bundle hash generation')
180
+ }
181
+
182
+ let input = secret
183
+ if (source) {
184
+ input = secret + source
185
+ }
186
+
187
+ // Fix: BUNDLE_HASH_LENGTH is 64 (hex chars), but shake256 expects bits
188
+ // So we multiply by 4: 64 hex chars * 4 = 256 bits (matching JS SDK)
189
+ const hash = shake256(input, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH * 4)
190
+ return hash as BundleHash
191
+ }
192
+
193
+ /**
194
+ * Enhanced bundle hash generation with options
195
+ */
196
+ export function generateBundleHashEnhanced(options: BundleHashOptions): BundleHash {
197
+ const { secret, source = null, algorithm = 'SHAKE256' } = options
198
+
199
+ if (algorithm !== 'SHAKE256') {
200
+ throw new Error('Only SHAKE256 algorithm is supported for cross-platform compatibility')
201
+ }
202
+
203
+ return generateBundleHash(secret, source)
204
+ }
205
+
206
+ // =============================================================================
207
+ // POSITION GENERATION - CRYPTOGRAPHICALLY SECURE RANDOM POSITIONS
208
+ // =============================================================================
209
+
210
+ /**
211
+ * Generate cryptographically secure position
212
+ * Must be 64 hex characters (256 bits)
213
+ */
214
+ export function generatePosition(): Position {
215
+ // Generate 32 random bytes (256 bits) and convert to hex
216
+ const position = randomString(CRYPTO_CONSTANTS.POSITION_LENGTH, '0123456789abcdef')
217
+ return position as Position
218
+ }
219
+
220
+ /**
221
+ * Enhanced position generation with options
222
+ */
223
+ export function generatePositionEnhanced(options: PositionGenerationOptions = {}): Position {
224
+ const {
225
+ entropy = null,
226
+ algorithm = 'SHAKE256',
227
+ length = CRYPTO_CONSTANTS.POSITION_LENGTH
228
+ } = options
229
+
230
+ if (entropy) {
231
+ // Use provided entropy with SHAKE256
232
+ const entropyString = Array.from(entropy).map(b => b.toString(16).padStart(2, '0')).join('')
233
+ const hash = shake256(entropyString, length)
234
+ return hash as Position
235
+ }
236
+
237
+ if (length !== CRYPTO_CONSTANTS.POSITION_LENGTH) {
238
+ throw new Error(`Position length must be exactly ${CRYPTO_CONSTANTS.POSITION_LENGTH} characters`)
239
+ }
240
+
241
+ return generatePosition()
242
+ }
243
+
244
+ // =============================================================================
245
+ // BATCH ID GENERATION - DETERMINISTIC BATCH IDENTIFICATION
246
+ // =============================================================================
247
+
248
+ /**
249
+ * Generate batch ID from molecular hash and index
250
+ * Uses SHAKE256 to create deterministic batch identifiers
251
+ */
252
+ export function generateBatchId({
253
+ molecularHash = null,
254
+ index = null
255
+ }: {
256
+ molecularHash?: string | null
257
+ index?: number | null
258
+ } = {}): string {
259
+ let input = ''
260
+
261
+ if (molecularHash) {
262
+ input += molecularHash
263
+ }
264
+
265
+ if (index !== null) {
266
+ input += index.toString()
267
+ }
268
+
269
+ // If no input provided, generate random batch ID
270
+ if (input === '') {
271
+ input = randomString(32)
272
+ }
273
+
274
+ // Use SHAKE256 to generate 64-character batch ID
275
+ return shake256(input, 64)
276
+ }
277
+
278
+ // =============================================================================
279
+ // WALLET KEY GENERATION - MUST MATCH JavaScript SDK EXACTLY
280
+ // =============================================================================
281
+
282
+ /**
283
+ * Generate wallet key from secret, token, and position
284
+ * MUST match JS SDK algorithm exactly for cross-platform compatibility
285
+ *
286
+ * Algorithm from JS SDK Wallet.generateKey():
287
+ * 1. Convert secret to BigInt
288
+ * 2. Add position as BigInt
289
+ * 3. Convert result to hex string
290
+ * 4. Hash with SHAKE256 (8192 bits)
291
+ * 5. If token provided, append token and hash again
292
+ * 6. Final hash with SHAKE256 (8192 bits)
293
+ */
294
+ export function generateWalletKey({
295
+ secret,
296
+ token,
297
+ position
298
+ }: {
299
+ secret: string
300
+ token: string | null
301
+ position: string
302
+ }): string {
303
+ if (!secret || !position) {
304
+ throw new Error('Secret and position are required for key generation')
305
+ }
306
+
307
+ try {
308
+ // Step 1: Convert secret to BigInt (must match JS SDK)
309
+ const bigIntSecret = BigInt(`0x${secret}`)
310
+
311
+ // Step 2: Add position as BigInt
312
+ const indexedKey = bigIntSecret + BigInt(`0x${position}`)
313
+
314
+ // Step 3: Create intermediate key sponge
315
+ const intermediateKeySponge = new JsSHA('SHAKE256', 'TEXT')
316
+
317
+ // Step 4: Update with hex string of indexed key
318
+ intermediateKeySponge.update(indexedKey.toString(16))
319
+
320
+ // Step 5: If token provided, update the SAME sponge with token
321
+ if (token) {
322
+ intermediateKeySponge.update(token)
323
+ }
324
+
325
+ // Step 6: Get intermediate hash (8192 bits = 2048 hex chars)
326
+ // Step 7: Create new sponge for private key
327
+ const privateKeySponge = new JsSHA('SHAKE256', 'TEXT')
328
+ privateKeySponge.update(intermediateKeySponge.getHash('HEX', { outputLen: 8192 }))
329
+
330
+ // Return final key (8192 bits = 2048 hex chars) - JS SDK doesn't call toLowerCase()
331
+ return privateKeySponge.getHash('HEX', { outputLen: 8192 })
332
+
333
+ } catch (error) {
334
+ throw new Error(`Key generation failed: ${error instanceof Error ? error.message : String(error)}`)
335
+ }
336
+ }
337
+
338
+ // =============================================================================
339
+ // WALLET ADDRESS GENERATION - MUST MATCH JavaScript SDK EXACTLY
340
+ // =============================================================================
341
+
342
+ /**
343
+ * Generate wallet address from key
344
+ * MUST match JS SDK algorithm exactly for cross-platform compatibility
345
+ *
346
+ * Algorithm from JS SDK Wallet.generateAddress():
347
+ * 1. Split key into 16 fragments of 128 characters each
348
+ * 2. For each fragment, hash 16 times with SHAKE256
349
+ * 3. Combine all fragments and hash with SHAKE256
350
+ * 4. Final hash with SHAKE256 (256 bits = 64 hex chars)
351
+ */
352
+ export function generateWalletAddress(key: string): string {
353
+ if (!key || key.length < 2048) {
354
+ throw new Error('Key must be at least 2048 characters for address generation')
355
+ }
356
+
357
+ try {
358
+ // Step 1: Split into 16 fragments of 128 characters each
359
+ const keyFragments = []
360
+ for (let i = 0; i < key.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
361
+ keyFragments.push(key.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
362
+ }
363
+
364
+ // Step 2: Hash each fragment 16 times
365
+ const digestSponge = new JsSHA('SHAKE256', 'TEXT')
366
+
367
+ for (const fragment of keyFragments) {
368
+ let workingFragment = fragment
369
+
370
+ // Hash the fragment 16 times
371
+ for (let fragmentCount = 1; fragmentCount <= CRYPTO_CONSTANTS.HASH_ITERATIONS; fragmentCount++) {
372
+ const workingSponge = new JsSHA('SHAKE256', 'TEXT')
373
+ workingSponge.update(workingFragment)
374
+ workingFragment = workingSponge.getHash('HEX', { outputLen: 512 })
375
+ }
376
+
377
+ // Add processed fragment to digest
378
+ digestSponge.update(workingFragment)
379
+ }
380
+
381
+ // Step 3: Get intermediate digest
382
+ const intermediateDigest = digestSponge.getHash('HEX', { outputLen: 8192 })
383
+
384
+ // Step 4: Final hash to create address
385
+ const outputSponge = new JsSHA('SHAKE256', 'TEXT')
386
+ outputSponge.update(intermediateDigest)
387
+
388
+ return outputSponge.getHash('HEX', { outputLen: CRYPTO_CONSTANTS.ADDRESS_LENGTH * 4 })
389
+
390
+ } catch (error) {
391
+ throw new Error(`Address generation failed: ${error instanceof Error ? error.message : String(error)}`)
392
+ }
393
+ }
394
+
395
+ // =============================================================================
396
+ // VALIDATION FUNCTIONS - ENSURE DATA INTEGRITY
397
+ // =============================================================================
398
+
399
+ /**
400
+ * Validate SHAKE256 hash format and length
401
+ */
402
+ export function validateSHAKE256Hash(hash: string, expectedLength: number): boolean {
403
+ if (!hash || typeof hash !== 'string') return false
404
+ if (hash.length !== expectedLength) return false
405
+ return /^[0-9a-f]+$/i.test(hash)
406
+ }
407
+
408
+ /**
409
+ * Validate secret format and length
410
+ */
411
+ export function validateSecret(secret: string): boolean {
412
+ if (!secret || typeof secret !== 'string') return false
413
+ if (secret.length !== CRYPTO_CONSTANTS.SECRET_LENGTH) return false
414
+ return /^[0-9a-f]+$/i.test(secret)
415
+ }
416
+
417
+ /**
418
+ * Validate bundle hash format
419
+ */
420
+ export function validateBundleHash(bundleHash: string): boolean {
421
+ return validateSHAKE256Hash(bundleHash, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH)
422
+ }
423
+
424
+ /**
425
+ * Validate position format
426
+ */
427
+ export function validatePosition(position: string): boolean {
428
+ return validateSHAKE256Hash(position, CRYPTO_CONSTANTS.POSITION_LENGTH)
429
+ }
430
+
431
+ /**
432
+ * Validate wallet address format
433
+ */
434
+ export function validateWalletAddress(address: string): boolean {
435
+ return validateSHAKE256Hash(address, CRYPTO_CONSTANTS.ADDRESS_LENGTH)
436
+ }
437
+
438
+ // =============================================================================
439
+ // COMPATIBILITY TESTING - VERIFY CROSS-PLATFORM COMPATIBILITY
440
+ // =============================================================================
441
+
442
+ /**
443
+ * Test vector for SHAKE256 compatibility
444
+ * These values MUST match across all SDK implementations
445
+ */
446
+ export const COMPATIBILITY_TEST_VECTORS = {
447
+ shake256: [
448
+ {
449
+ input: 'test',
450
+ outputLength: 256,
451
+ expected: 'b54ff7255705a71ee2925e4a3e30e41aed489a579d5595e0df13e32e1e4dd202'
452
+ },
453
+ {
454
+ input: 'KnishIO',
455
+ outputLength: 256,
456
+ expected: '35e3c3f33aefb940baaf430855ccb441c24b7b0542f682b8543f4c9d3a077c6e'
457
+ }
458
+ ]
459
+ } as const
460
+
461
+ /**
462
+ * Run compatibility tests to verify cross-platform consistency
463
+ */
464
+ export function runCompatibilityTests(): { passed: boolean; results: Array<{ test: string; passed: boolean; expected: string; actual: string }> } {
465
+ const results = []
466
+ let allPassed = true
467
+
468
+ for (const testVector of COMPATIBILITY_TEST_VECTORS.shake256) {
469
+ const actual = shake256(testVector.input, testVector.outputLength)
470
+ const passed = actual === testVector.expected
471
+
472
+ results.push({
473
+ test: `shake256("${testVector.input}", ${testVector.outputLength})`,
474
+ passed,
475
+ expected: testVector.expected,
476
+ actual
477
+ })
478
+
479
+ if (!passed) {
480
+ allPassed = false
481
+ }
482
+ }
483
+
484
+ return { passed: allPassed, results }
485
+ }
486
+
487
+ // =============================================================================
488
+ // BASE17 CONVERSION SYSTEM - CRITICAL FOR MOLECULAR HASH PROCESSING
489
+ // =============================================================================
490
+
491
+ /**
492
+ * Base conversion utility for molecular hash processing
493
+ * MUST match JavaScript SDK implementation exactly
494
+ */
495
+ export function charsetBaseConvert(
496
+ input: string,
497
+ fromBase: number,
498
+ toBase: number,
499
+ fromCharset: string,
500
+ toCharset: string
501
+ ): string {
502
+ if (input === '') return toCharset[0]
503
+
504
+ // Convert input from source base to decimal using BigInt for precision
505
+ let decimal = 0n
506
+ for (let i = 0; i < input.length; i++) {
507
+ const char = input[i]
508
+ const value = BigInt(fromCharset.indexOf(char))
509
+ if (value === -1n) {
510
+ throw new Error(`Invalid character '${char}' for source base ${fromBase}`)
511
+ }
512
+ decimal = decimal * BigInt(fromBase) + value
513
+ }
514
+
515
+ // Convert decimal to target base
516
+ if (decimal === 0n) return toCharset[0]
517
+
518
+ let result = ''
519
+ while (decimal > 0n) {
520
+ const remainder = decimal % BigInt(toBase)
521
+ result = toCharset[Number(remainder)] + result
522
+ decimal = decimal / BigInt(toBase)
523
+ }
524
+
525
+ return result
526
+ }
527
+
528
+ /**
529
+ * Convert molecular hash from base16 to base17
530
+ * Critical for WOTS+ signature generation
531
+ */
532
+ export function convertToBase17(hexHash: string): string {
533
+ const base16Charset = '0123456789abcdef'
534
+ const base17Charset = '0123456789abcdefg'
535
+
536
+ const base17Hash = charsetBaseConvert(hexHash, 16, 17, base16Charset, base17Charset)
537
+
538
+ // Pad to 64 characters as required by Implementation Guide
539
+ return base17Hash.padStart(64, '0')
540
+ }
541
+
542
+ // =============================================================================
543
+ // MOLECULAR HASH ENUMERATION AND NORMALIZATION - FOR WOTS+ SIGNATURES
544
+ // =============================================================================
545
+
546
+ /**
547
+ * Enumerate molecular hash for signature generation
548
+ * MUST match JavaScript SDK Molecule.enumerate() exactly
549
+ */
550
+ export function enumerateMolecularHash(hash: string): number[] {
551
+ const mapped: Record<string, number> = {
552
+ '0': -8, '1': -7, '2': -6, '3': -5, '4': -4, '5': -3, '6': -2, '7': -1,
553
+ '8': 0, '9': 1, 'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8
554
+ }
555
+
556
+ const target: number[] = []
557
+ const hashList = hash.toLowerCase().split('')
558
+
559
+ for (let index = 0; index < hashList.length; index++) {
560
+ const symbol = hashList[index]
561
+ if (typeof mapped[symbol] !== 'undefined') {
562
+ target[index] = mapped[symbol]
563
+ } else {
564
+ throw new Error(`Invalid character '${symbol}' in molecular hash`)
565
+ }
566
+ }
567
+
568
+ return target
569
+ }
570
+
571
+ /**
572
+ * Normalize enumerated molecular hash for signature generation
573
+ * MUST match JavaScript SDK Molecule.normalize() exactly
574
+ */
575
+ export function normalizeMolecularHash(mappedHashArray: number[]): number[] {
576
+ // Create a copy to avoid mutating the original
577
+ const normalized = [...mappedHashArray]
578
+
579
+ let total = normalized.reduce((sum, num) => sum + num, 0)
580
+ const totalCondition = total < 0
581
+
582
+ while (total !== 0) {
583
+ for (let index = 0; index < normalized.length; index++) {
584
+ const condition = totalCondition
585
+ ? normalized[index] < 8
586
+ : normalized[index] > -8
587
+
588
+ if (condition) {
589
+ if (totalCondition) {
590
+ normalized[index]++
591
+ total++
592
+ } else {
593
+ normalized[index]--
594
+ total--
595
+ }
596
+
597
+ if (total === 0) break
598
+ }
599
+ }
600
+ }
601
+
602
+ return normalized
603
+ }
604
+
605
+ // =============================================================================
606
+ // WOTS+ ONE-TIME SIGNATURE IMPLEMENTATION - CRITICAL FOR TRANSACTION SIGNING
607
+ // =============================================================================
608
+
609
+ /**
610
+ * Generate WOTS+ one-time signature for molecular hash
611
+ * MUST match JavaScript SDK implementation exactly for cross-platform compatibility
612
+ */
613
+ export function generateOTSSignature(privateKey: string, molecularHash: string): string {
614
+ if (!privateKey || privateKey.length < 2048) {
615
+ throw new Error('Private key must be at least 2048 characters for OTS signature')
616
+ }
617
+
618
+ if (!molecularHash || molecularHash.length !== 64) {
619
+ throw new Error('Molecular hash must be exactly 64 characters')
620
+ }
621
+
622
+ try {
623
+ // Step 1: Molecular hash is already base17 from Atom.hashAtoms(), enumerate and normalize it
624
+ // Fix: Don't convert to base17 again - it's already base17 (matching JS SDK behavior)
625
+ const enumerated = enumerateMolecularHash(molecularHash)
626
+ const normalized = normalizeMolecularHash(enumerated)
627
+
628
+ // Step 2: Subdivide private key into 16 chunks of 128 characters each
629
+ const keyChunks: string[] = []
630
+ for (let i = 0; i < privateKey.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
631
+ keyChunks.push(privateKey.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
632
+ }
633
+
634
+ // Step 3: Generate signature fragments
635
+ let signatureFragments = ''
636
+ for (let index = 0; index < keyChunks.length; index++) {
637
+ let workingChunk = keyChunks[index]
638
+
639
+ // Hash (8 - normalized[index]) times for signature generation
640
+ const iterations = 8 - (normalized[index] || 0)
641
+ for (let j = 0; j < iterations; j++) {
642
+ const sponge = new JsSHA('SHAKE256', 'TEXT')
643
+ sponge.update(workingChunk)
644
+ workingChunk = sponge.getHash('HEX', { outputLen: 512 }) // 512 bits
645
+ }
646
+ signatureFragments += workingChunk
647
+ }
648
+
649
+ return signatureFragments
650
+ } catch (error) {
651
+ throw new Error(`OTS signature generation failed: ${error instanceof Error ? error.message : String(error)}`)
652
+ }
653
+ }
654
+
655
+ /**
656
+ * Verify WOTS+ one-time signature against molecular hash and address
657
+ * MUST match JavaScript SDK implementation exactly
658
+ */
659
+ export function verifyOTSSignature(
660
+ otsFragments: string,
661
+ molecularHash: string,
662
+ signingAddress: string
663
+ ): boolean {
664
+ if (!otsFragments || !molecularHash || !signingAddress) {
665
+ return false
666
+ }
667
+
668
+ try {
669
+ // Step 1: Convert molecular hash to base17 and normalize
670
+ const base17Hash = convertToBase17(molecularHash)
671
+ const enumerated = enumerateMolecularHash(base17Hash)
672
+ const normalized = normalizeMolecularHash(enumerated)
673
+
674
+ // Step 2: Process OTS fragments (handle compression if needed)
675
+ let ots = otsFragments
676
+ if (ots.length !== 2048) {
677
+ // If not 2048 chars, it might be base64 compressed - for now, assume uncompressed
678
+ if (ots.length !== 2048) {
679
+ return false // Invalid signature length
680
+ }
681
+ }
682
+
683
+ // Step 3: Subdivide OTS into 16 segments of 128 characters each
684
+ const otsChunks: string[] = []
685
+ for (let i = 0; i < ots.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
686
+ otsChunks.push(ots.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
687
+ }
688
+
689
+ // Step 4: Process each chunk for verification
690
+ let keyFragments = ''
691
+ for (let index = 0; index < otsChunks.length; index++) {
692
+ let workingChunk = otsChunks[index]
693
+
694
+ // Hash (8 + normalized[index]) times for verification
695
+ const iterations = 8 + (normalized[index] || 0)
696
+ for (let j = 0; j < iterations; j++) {
697
+ const sponge = new JsSHA('SHAKE256', 'TEXT')
698
+ sponge.update(workingChunk)
699
+ workingChunk = sponge.getHash('HEX', { outputLen: 512 }) // 512 bits
700
+ }
701
+ keyFragments += workingChunk
702
+ }
703
+
704
+ // Step 5: Generate digest from reconstructed key fragments
705
+ const digestSponge = new JsSHA('SHAKE256', 'TEXT')
706
+ digestSponge.update(keyFragments)
707
+ const digest = digestSponge.getHash('HEX', { outputLen: 8192 }) // 8192 bits
708
+
709
+ // Step 6: Generate address from digest
710
+ const addressSponge = new JsSHA('SHAKE256', 'TEXT')
711
+ addressSponge.update(digest)
712
+ const reconstructedAddress = addressSponge.getHash('HEX', { outputLen: 256 }) // 256 bits
713
+
714
+ // Step 7: Compare with expected signing address
715
+ return reconstructedAddress.toLowerCase() === signingAddress.toLowerCase()
716
+ } catch (error) {
717
+ return false
718
+ }
719
+ }
720
+
721
+ // =============================================================================
722
+ // ENHANCED VALIDATION FUNCTIONS - SUPPORT FOR NEW SIGNATURE SYSTEM
723
+ // =============================================================================
724
+
725
+ /**
726
+ * Validate OTS signature format
727
+ */
728
+ export function validateOTSSignature(signature: string): boolean {
729
+ if (!signature || typeof signature !== 'string') return false
730
+
731
+ // OTS signature should be 2048 characters (16 chunks * 128 chars each)
732
+ // or compressed format
733
+ return signature.length === 2048 || (signature.length > 0 && signature.length < 2048)
734
+ }
735
+
736
+ /**
737
+ * Validate molecular hash for base17 conversion
738
+ */
739
+ export function validateMolecularHashForSignature(hash: string): boolean {
740
+ if (!hash || typeof hash !== 'string') return false
741
+ if (hash.length !== 64) return false
742
+ return /^[0-9a-f]+$/i.test(hash)
743
+ }
744
+
745
+ // =============================================================================
746
+ // UPDATED COMPATIBILITY TEST VECTORS - INCLUDING SIGNATURE TESTS
747
+ // =============================================================================
748
+
749
+ /**
750
+ * Extended test vectors including signature generation
751
+ */
752
+ export const EXTENDED_COMPATIBILITY_TEST_VECTORS = {
753
+ ...COMPATIBILITY_TEST_VECTORS,
754
+ base17Conversion: [
755
+ {
756
+ input: '0000000000000000000000000000000000000000000000000000000000000000',
757
+ expected: '0000000000000000000000000000000000000000000000000000000000000000'
758
+ }
759
+ ],
760
+ molecularEnumeration: [
761
+ {
762
+ input: '0000000000000000000000000000000000000000000000000000000000000000',
763
+ expected: [-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8]
764
+ }
765
+ ]
766
+ } as const
767
+
768
+ /**
769
+ * Run extended compatibility tests including signature system
770
+ */
771
+ export function runExtendedCompatibilityTests(): {
772
+ passed: boolean
773
+ results: Array<{ test: string; passed: boolean; expected: any; actual: any }>
774
+ } {
775
+ const results = []
776
+ let allPassed = true
777
+
778
+ // Run original SHAKE256 tests
779
+ const originalResults = runCompatibilityTests()
780
+ results.push(...originalResults.results)
781
+ if (!originalResults.passed) allPassed = false
782
+
783
+ // Test base17 conversion
784
+ for (const testVector of EXTENDED_COMPATIBILITY_TEST_VECTORS.base17Conversion) {
785
+ const actual = convertToBase17(testVector.input)
786
+ const passed = actual === testVector.expected
787
+
788
+ results.push({
789
+ test: `convertToBase17("${testVector.input}")`,
790
+ passed,
791
+ expected: testVector.expected,
792
+ actual
793
+ })
794
+
795
+ if (!passed) allPassed = false
796
+ }
797
+
798
+ // Test molecular enumeration
799
+ for (const testVector of EXTENDED_COMPATIBILITY_TEST_VECTORS.molecularEnumeration) {
800
+ const actual = enumerateMolecularHash(testVector.input)
801
+ const passed = JSON.stringify(actual) === JSON.stringify(testVector.expected)
802
+
803
+ results.push({
804
+ test: `enumerateMolecularHash("${testVector.input}")`,
805
+ passed,
806
+ expected: testVector.expected,
807
+ actual
808
+ })
809
+
810
+ if (!passed) allPassed = false
811
+ }
812
+
813
+ return { passed: allPassed, results }
814
+ }
815
+
816
+ // All functions are already exported individually above