@wishknish/knishio-client-ts 1.2.1 → 1.3.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.
- package/dist/index.cjs +142 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -11
- package/dist/index.d.ts +12 -11
- package/dist/index.iife.js +142 -121
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +142 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/AuthToken.ts +6 -2
- package/src/KnishIOClient.ts +151 -80
- package/src/core/AtomMeta.ts +0 -19
- package/src/core/Molecule.ts +6 -35
- package/src/index.ts +1 -1
- package/src/libraries/CheckMolecule.ts +5 -19
- package/src/mutation/MutationWithdrawBufferToken.ts +3 -7
- package/src/query/QueryContinuId.ts +2 -2
- package/src/response/ResponseProposeMolecule.ts +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wishknish/knishio-client-ts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"productName": "Knish.IO TypeScript SDK Client",
|
|
5
5
|
"description": "TypeScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs with enhanced type safety.",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
package/src/AuthToken.ts
CHANGED
|
@@ -53,7 +53,9 @@ import Wallet from '@/core/Wallet'
|
|
|
53
53
|
* so the two can never drift: a field either round-trips or neither side compiles.
|
|
54
54
|
*
|
|
55
55
|
* `wallet.mlKemParameterSet` is optional because snapshots persisted before ML-KEM-1024 became
|
|
56
|
-
* the default do not carry it — see {@link AuthToken.resolveMlKemParameterSet}.
|
|
56
|
+
* the default do not carry it — see {@link AuthToken.resolveMlKemParameterSet}. `wallet.token` is
|
|
57
|
+
* optional because snapshots persisted before re-logins were signed from the USER ContinuID
|
|
58
|
+
* wallet do not carry it; every such session was signed from an AUTH wallet.
|
|
57
59
|
*/
|
|
58
60
|
export type AuthTokenSnapshot = {
|
|
59
61
|
token: string
|
|
@@ -61,6 +63,7 @@ export type AuthTokenSnapshot = {
|
|
|
61
63
|
pubkey: string
|
|
62
64
|
encrypt: boolean
|
|
63
65
|
wallet?: {
|
|
66
|
+
token?: string
|
|
64
67
|
position: string | null
|
|
65
68
|
characters: string | null
|
|
66
69
|
mlKemParameterSet?: 1024 | 768
|
|
@@ -134,7 +137,7 @@ export default class AuthToken {
|
|
|
134
137
|
static restore(snapshot: AuthTokenSnapshot, secret: string): AuthToken {
|
|
135
138
|
const wallet = new Wallet({
|
|
136
139
|
secret,
|
|
137
|
-
token: 'AUTH',
|
|
140
|
+
token: snapshot.wallet?.token ?? 'AUTH',
|
|
138
141
|
position: snapshot.wallet?.position ?? null,
|
|
139
142
|
characters: snapshot.wallet?.characters ?? null,
|
|
140
143
|
mlKemParameterSet: AuthToken.resolveMlKemParameterSet(snapshot)
|
|
@@ -232,6 +235,7 @@ export default class AuthToken {
|
|
|
232
235
|
encrypt: this.$__encrypt,
|
|
233
236
|
...(this.$__wallet ? {
|
|
234
237
|
wallet: {
|
|
238
|
+
token: this.$__wallet.token,
|
|
235
239
|
position: this.$__wallet.position,
|
|
236
240
|
characters: this.$__wallet.characters,
|
|
237
241
|
mlKemParameterSet: this.$__wallet.mlKemParameterSet
|
package/src/KnishIOClient.ts
CHANGED
|
@@ -1014,38 +1014,41 @@ export default class KnishIOClient {
|
|
|
1014
1014
|
this.setCellSlug(cellSlug)
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
|
-
// Auth in process...
|
|
1017
|
+
// Auth in process... Cleared in `finally`: a rejected login must not leave the guard set, or
|
|
1018
|
+
// executeQuery never refreshes an expired token again.
|
|
1018
1019
|
this.$__authInProcess = true
|
|
1019
1020
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
// Log success
|
|
1038
|
-
if (this.$__authToken) {
|
|
1039
|
-
this.log('info', `KnishIOClient::requestAuthToken() - Successfully retrieved auth token...`)
|
|
1040
|
-
}
|
|
1021
|
+
try {
|
|
1022
|
+
// Auth token response
|
|
1023
|
+
let response: Response
|
|
1024
|
+
|
|
1025
|
+
// Authorized user (profile auth with secret)
|
|
1026
|
+
if (secret) {
|
|
1027
|
+
response = await this.requestProfileAuthToken({
|
|
1028
|
+
secret,
|
|
1029
|
+
encrypt
|
|
1030
|
+
})
|
|
1031
|
+
} else {
|
|
1032
|
+
// Guest auth (no secret)
|
|
1033
|
+
response = await this.requestGuestAuthToken({
|
|
1034
|
+
cellSlug: cellSlug || this.getCellSlug(),
|
|
1035
|
+
encrypt
|
|
1036
|
+
})
|
|
1037
|
+
}
|
|
1041
1038
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1039
|
+
// Log success
|
|
1040
|
+
if (this.$__authToken) {
|
|
1041
|
+
this.log('info', `KnishIOClient::requestAuthToken() - Successfully retrieved auth token...`)
|
|
1042
|
+
}
|
|
1044
1043
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1044
|
+
// Handle encryption if needed
|
|
1045
|
+
this.switchEncryption(encrypt)
|
|
1047
1046
|
|
|
1048
|
-
|
|
1047
|
+
return response
|
|
1048
|
+
} finally {
|
|
1049
|
+
// Auth process is stopped
|
|
1050
|
+
this.$__authInProcess = false
|
|
1051
|
+
}
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
1051
1054
|
/**
|
|
@@ -1233,17 +1236,20 @@ export default class KnishIOClient {
|
|
|
1233
1236
|
|
|
1234
1237
|
/**
|
|
1235
1238
|
* Query next ContinuId wallet
|
|
1239
|
+
*
|
|
1240
|
+
* @param token - Restricts the lookup to wallets of this token. Without it the validator falls
|
|
1241
|
+
* back to the bundle's newest wallet of ANY token when the bundle has no ContinuID pointer.
|
|
1236
1242
|
*/
|
|
1237
1243
|
async queryContinuId({
|
|
1238
|
-
bundle
|
|
1244
|
+
bundle,
|
|
1245
|
+
token = null
|
|
1239
1246
|
}: {
|
|
1240
1247
|
bundle: BundleHash | string
|
|
1248
|
+
token?: string | null
|
|
1241
1249
|
}): Promise<Response> {
|
|
1242
1250
|
const query = this.createQuery(QueryContinuId)
|
|
1243
1251
|
|
|
1244
|
-
return this.executeQuery(query, {
|
|
1245
|
-
bundle
|
|
1246
|
-
}) as Promise<Response>
|
|
1252
|
+
return this.executeQuery(query, token ? { bundle, token } : { bundle }) as Promise<Response>
|
|
1247
1253
|
}
|
|
1248
1254
|
|
|
1249
1255
|
/**
|
|
@@ -2433,13 +2439,11 @@ export default class KnishIOClient {
|
|
|
2433
2439
|
async withdrawBufferToken({
|
|
2434
2440
|
tokenSlug,
|
|
2435
2441
|
amount,
|
|
2436
|
-
sourceWallet = null
|
|
2437
|
-
signingWallet = null
|
|
2442
|
+
sourceWallet = null
|
|
2438
2443
|
}: {
|
|
2439
2444
|
tokenSlug: TokenSlug | string
|
|
2440
2445
|
amount: number | string
|
|
2441
2446
|
sourceWallet?: Wallet | null
|
|
2442
|
-
signingWallet?: Wallet | null
|
|
2443
2447
|
}): Promise<Response> {
|
|
2444
2448
|
this.log('info', `KnishIOClient::withdrawBufferToken() - Withdrawing ${amount} of ${tokenSlug} from buffer...`)
|
|
2445
2449
|
|
|
@@ -2454,12 +2458,7 @@ export default class KnishIOClient {
|
|
|
2454
2458
|
// Initialize the withdraw buffer token mutation
|
|
2455
2459
|
const recipients: Record<string, any> = {}
|
|
2456
2460
|
recipients[this.getBundle()] = amount
|
|
2457
|
-
|
|
2458
|
-
const fillMoleculeParams: any = { recipients }
|
|
2459
|
-
if (signingWallet) {
|
|
2460
|
-
fillMoleculeParams.signingWallet = signingWallet
|
|
2461
|
-
}
|
|
2462
|
-
await mutation.fillMolecule(fillMoleculeParams)
|
|
2461
|
+
await mutation.fillMolecule({ recipients })
|
|
2463
2462
|
|
|
2464
2463
|
// Execute the mutation
|
|
2465
2464
|
const response = await this.executeQuery(mutation)
|
|
@@ -2487,29 +2486,43 @@ export default class KnishIOClient {
|
|
|
2487
2486
|
} = {}): Promise<Response> {
|
|
2488
2487
|
this.log('info', 'KnishIOClient::requestGuestAuthToken() - Requesting guest auth token...')
|
|
2489
2488
|
|
|
2490
|
-
//
|
|
2491
|
-
|
|
2489
|
+
// Hold the auth guard so this login's own request does not start a nested token refresh.
|
|
2490
|
+
// Restore the caller's value: requestAuthToken holds it until its own `finally`.
|
|
2491
|
+
const authInProcess = this.$__authInProcess
|
|
2492
|
+
this.$__authInProcess = true
|
|
2493
|
+
try {
|
|
2494
|
+
// Create the mutation (extends Mutation directly, not MutationProposeMolecule)
|
|
2495
|
+
const mutation = this.createQuery(MutationRequestAuthorizationGuest)
|
|
2492
2496
|
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
+
// Execute with variables
|
|
2498
|
+
const response = await this.executeQuery(mutation, {
|
|
2499
|
+
cellSlug: cellSlug || this.getCellSlug()
|
|
2500
|
+
})
|
|
2497
2501
|
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2502
|
+
if (!response) {
|
|
2503
|
+
throw new CodeException('Guest auth token request failed')
|
|
2504
|
+
}
|
|
2501
2505
|
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
+
// Handle encryption if needed
|
|
2507
|
+
if (encrypt) {
|
|
2508
|
+
this.switchEncryption(true)
|
|
2509
|
+
}
|
|
2506
2510
|
|
|
2507
|
-
|
|
2511
|
+
return response
|
|
2512
|
+
} finally {
|
|
2513
|
+
this.$__authInProcess = authInProcess
|
|
2514
|
+
}
|
|
2508
2515
|
}
|
|
2509
2516
|
|
|
2510
2517
|
/**
|
|
2511
2518
|
* Request profile authentication token
|
|
2512
|
-
*
|
|
2519
|
+
*
|
|
2520
|
+
* A returning user (the bundle has a USER ContinuID pointer) signs from the USER wallet
|
|
2521
|
+
* registered at that pointer, so validator 0.5.0+ marks the token proven and the identity keeps
|
|
2522
|
+
* read/subscription access to permissioned and private cells. A first login — or a pointer this
|
|
2523
|
+
* secret does not own — signs from a fresh AUTH wallet as before. A rejected pointer-signed
|
|
2524
|
+
* login falls back to the AUTH login exactly once, so one call sends at most two
|
|
2525
|
+
* authorization molecules (testnet allows 3 auths/min/IP).
|
|
2513
2526
|
*/
|
|
2514
2527
|
async requestProfileAuthToken({
|
|
2515
2528
|
secret,
|
|
@@ -2523,16 +2536,70 @@ export default class KnishIOClient {
|
|
|
2523
2536
|
// Set the secret
|
|
2524
2537
|
this.setSecret(secret)
|
|
2525
2538
|
|
|
2526
|
-
//
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2539
|
+
// Hold the auth guard so this login's own requests do not start a nested token refresh (a
|
|
2540
|
+
// second login that would sign at the same pointer). Restore the caller's value:
|
|
2541
|
+
// requestAuthToken holds it until its own `finally`.
|
|
2542
|
+
const authInProcess = this.$__authInProcess
|
|
2543
|
+
this.$__authInProcess = true
|
|
2544
|
+
try {
|
|
2545
|
+
// Filtered to USER: unfiltered, a bundle without a pointer answers with its newest wallet of
|
|
2546
|
+
// any token, which is not an identity wallet the validator would accept as the signer.
|
|
2547
|
+
const pointer = (await this.queryContinuId({
|
|
2548
|
+
bundle: this.getBundle(),
|
|
2549
|
+
token: 'USER'
|
|
2550
|
+
}))?.payload() as Wallet | null
|
|
2551
|
+
|
|
2552
|
+
let pointerWallet: Wallet | null = null
|
|
2553
|
+
if (pointer && pointer.token === 'USER' && pointer.position) {
|
|
2554
|
+
const wallet = new Wallet({
|
|
2555
|
+
secret,
|
|
2556
|
+
token: 'USER',
|
|
2557
|
+
position: pointer.position,
|
|
2558
|
+
mlKemParameterSet: this.getMlKemParameterSet()
|
|
2559
|
+
})
|
|
2560
|
+
if (!pointer.address || wallet.address === pointer.address) {
|
|
2561
|
+
pointerWallet = wallet
|
|
2562
|
+
} else {
|
|
2563
|
+
this.log('warn', 'KnishIOClient::requestProfileAuthToken() - ContinuID wallet address does not derive from this secret; signing from a fresh AUTH wallet')
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
if (pointerWallet) {
|
|
2568
|
+
const response = await this.proposeProfileAuthorization(pointerWallet, encrypt)
|
|
2569
|
+
if (response.success()) {
|
|
2570
|
+
this.log('info', 'KnishIOClient::requestProfileAuthToken() - Auth token signed from the ContinuID pointer (proven)')
|
|
2571
|
+
return this.acceptProfileAuthorization(response, pointerWallet, encrypt)
|
|
2572
|
+
}
|
|
2573
|
+
this.log('warn', `KnishIOClient::requestProfileAuthToken() - Pointer-signed authorization rejected (${response.reason()}); falling back once to an AUTH-wallet login`)
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// Generate a signing wallet with AUTH token (matching JavaScript SDK)
|
|
2577
|
+
const wallet = new Wallet({
|
|
2578
|
+
secret,
|
|
2579
|
+
token: 'AUTH',
|
|
2580
|
+
mlKemParameterSet: this.getMlKemParameterSet()
|
|
2581
|
+
})
|
|
2582
|
+
const response = await this.proposeProfileAuthorization(wallet, encrypt)
|
|
2583
|
+
|
|
2584
|
+
if (!response.success()) {
|
|
2585
|
+
throw new AuthorizationRejectedException(
|
|
2586
|
+
`KnishIOClient::requestProfileAuthToken() - Authorization attempt rejected by ledger. Reason: ${response.reason()}`
|
|
2587
|
+
)
|
|
2588
|
+
}
|
|
2532
2589
|
|
|
2533
|
-
|
|
2590
|
+
this.log('info', `KnishIOClient::requestProfileAuthToken() - Auth token signed from a fresh AUTH wallet (${pointerWallet ? 'fallback after the pointer-signed login was rejected' : 'no usable ContinuID pointer'})`)
|
|
2591
|
+
return this.acceptProfileAuthorization(response, wallet, encrypt)
|
|
2592
|
+
} finally {
|
|
2593
|
+
this.$__authInProcess = authInProcess
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
/**
|
|
2598
|
+
* Signs and proposes a profile authorization molecule from the given source wallet.
|
|
2599
|
+
*/
|
|
2600
|
+
private async proposeProfileAuthorization(wallet: Wallet, encrypt: boolean): Promise<ResponseRequestAuthorization> {
|
|
2534
2601
|
const molecule = await this.createMolecule({
|
|
2535
|
-
secret,
|
|
2602
|
+
secret: this.getSecret(),
|
|
2536
2603
|
sourceWallet: wallet
|
|
2537
2604
|
})
|
|
2538
2605
|
|
|
@@ -2543,7 +2610,7 @@ export default class KnishIOClient {
|
|
|
2543
2610
|
})
|
|
2544
2611
|
|
|
2545
2612
|
// Initialize the profile auth mutation.
|
|
2546
|
-
// PQ-transport Phase E: convey the
|
|
2613
|
+
// PQ-transport Phase E: convey the source wallet's ML-KEM public key as a SIGNED
|
|
2547
2614
|
// walletPubkey U-atom meta, so the validator can encrypt CipherHash responses back to THIS
|
|
2548
2615
|
// wallet (the one that decrypts them). Signed → MITM can't swap the target. Conveyed
|
|
2549
2616
|
// unconditionally (parity with JS/Kotlin/PHP), so even a plaintext auth primes enc_pubkey.
|
|
@@ -2562,22 +2629,26 @@ export default class KnishIOClient {
|
|
|
2562
2629
|
throw new CodeException('Profile auth token request failed')
|
|
2563
2630
|
}
|
|
2564
2631
|
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
)
|
|
2580
|
-
|
|
2632
|
+
return response
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
/**
|
|
2636
|
+
* Binds the accepted authorization's token to the wallet that signed it.
|
|
2637
|
+
*/
|
|
2638
|
+
private acceptProfileAuthorization(
|
|
2639
|
+
response: ResponseRequestAuthorization,
|
|
2640
|
+
wallet: Wallet,
|
|
2641
|
+
encrypt: boolean
|
|
2642
|
+
): ResponseRequestAuthorization {
|
|
2643
|
+
// Create & set an auth token from the response data
|
|
2644
|
+
const authToken = AuthToken.create({
|
|
2645
|
+
token: response.token(),
|
|
2646
|
+
expiresAt: response.expiresAt(),
|
|
2647
|
+
encrypt: String(response.encrypt()) === 'true',
|
|
2648
|
+
pubkey: response.pubKey()
|
|
2649
|
+
}, wallet)
|
|
2650
|
+
this.setAuthToken(authToken)
|
|
2651
|
+
this.lastMoleculeQuery = null
|
|
2581
2652
|
|
|
2582
2653
|
// Handle encryption if needed
|
|
2583
2654
|
if (encrypt) {
|
package/src/core/AtomMeta.ts
CHANGED
|
@@ -148,25 +148,6 @@ export default class AtomMeta {
|
|
|
148
148
|
return this
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/**
|
|
152
|
-
* Set signing wallet metadata
|
|
153
|
-
* @param signingWallet - Wallet used for signing
|
|
154
|
-
* @return This instance for chaining
|
|
155
|
-
*/
|
|
156
|
-
setSigningWallet(signingWallet: any): AtomMeta {
|
|
157
|
-
this.merge({
|
|
158
|
-
signingWallet: JSON.stringify({
|
|
159
|
-
tokenSlug: signingWallet.token,
|
|
160
|
-
bundleHash: signingWallet.bundle,
|
|
161
|
-
address: signingWallet.address,
|
|
162
|
-
position: signingWallet.position,
|
|
163
|
-
pubkey: signingWallet.pubkey,
|
|
164
|
-
characters: signingWallet.characters
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
return this
|
|
168
|
-
}
|
|
169
|
-
|
|
170
151
|
/**
|
|
171
152
|
* Add policy metadata
|
|
172
153
|
* @param policy - Policy object to add
|
package/src/core/Molecule.ts
CHANGED
|
@@ -412,25 +412,8 @@ export default class Molecule {
|
|
|
412
412
|
throw new SignatureMalformedException('No atoms available for signing!')
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
//
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
// Get signing wallet from first atom's metas (matching JS SDK exactly)
|
|
419
|
-
// This handles buffer withdraw reconciliation where a different wallet signs
|
|
420
|
-
const signingWalletMeta = signingAtom.aggregatedMeta ? signingAtom.aggregatedMeta() : null
|
|
421
|
-
const signingWalletJson = signingWalletMeta?.signingWallet as string | undefined
|
|
422
|
-
if (signingWalletJson) {
|
|
423
|
-
try {
|
|
424
|
-
const parsedSigningWallet = JSON.parse(signingWalletJson)
|
|
425
|
-
if (parsedSigningWallet.position) {
|
|
426
|
-
signingPosition = parsedSigningWallet.position
|
|
427
|
-
}
|
|
428
|
-
} catch {
|
|
429
|
-
// Invalid JSON in signingWallet meta, use default position
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Signing position is required
|
|
415
|
+
// The private key comes from the first atom's own position
|
|
416
|
+
const signingPosition = signingAtom.position
|
|
434
417
|
if (!signingPosition) {
|
|
435
418
|
throw new SignatureMalformedException('Signing wallet must have a position!')
|
|
436
419
|
}
|
|
@@ -938,7 +921,7 @@ export default class Molecule {
|
|
|
938
921
|
metaType: string
|
|
939
922
|
metaId: string
|
|
940
923
|
rule: Rule[] | any[] | any // Accept single object or array
|
|
941
|
-
policy?: Record<string, any>
|
|
924
|
+
policy?: Record<string, any> | null
|
|
942
925
|
}): Molecule {
|
|
943
926
|
if (!this.sourceWallet) {
|
|
944
927
|
throw new Error('Source wallet required for createRule')
|
|
@@ -958,10 +941,8 @@ export default class Molecule {
|
|
|
958
941
|
rule: JSON.stringify(rules)
|
|
959
942
|
})
|
|
960
943
|
|
|
961
|
-
//
|
|
962
|
-
|
|
963
|
-
atomMeta.addPolicy(policy)
|
|
964
|
-
}
|
|
944
|
+
// JS createRule always adds the policy (its {} default still fills per-key defaults).
|
|
945
|
+
atomMeta.addPolicy(policy ?? {})
|
|
965
946
|
|
|
966
947
|
// Create R-isotope atom for rule (matching JS SDK)
|
|
967
948
|
this.addAtom(Atom.create({
|
|
@@ -1294,15 +1275,12 @@ export default class Molecule {
|
|
|
1294
1275
|
* Matches JavaScript SDK Molecule.initWithdrawBuffer implementation exactly
|
|
1295
1276
|
*
|
|
1296
1277
|
* @param recipients - Map of recipientBundle → amount
|
|
1297
|
-
* @param signingWallet - Optional signing wallet for reconciliation
|
|
1298
1278
|
* @return This molecule instance for chaining
|
|
1299
1279
|
*/
|
|
1300
1280
|
initWithdrawBuffer({
|
|
1301
|
-
recipients
|
|
1302
|
-
signingWallet = null
|
|
1281
|
+
recipients
|
|
1303
1282
|
}: {
|
|
1304
1283
|
recipients: Record<string, number>
|
|
1305
|
-
signingWallet?: any | null
|
|
1306
1284
|
}): Molecule {
|
|
1307
1285
|
if (!this.sourceWallet) {
|
|
1308
1286
|
throw new Error('Source wallet required for buffer withdrawal')
|
|
@@ -1318,18 +1296,11 @@ export default class Molecule {
|
|
|
1318
1296
|
throw new BalanceInsufficientException()
|
|
1319
1297
|
}
|
|
1320
1298
|
|
|
1321
|
-
// Set a meta signing position for molecule correct reconciliation
|
|
1322
|
-
const firstAtomMeta = new AtomMeta()
|
|
1323
|
-
if (signingWallet) {
|
|
1324
|
-
firstAtomMeta.setSigningWallet(signingWallet)
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
1299
|
// Debit full balance from source (B-isotope)
|
|
1328
1300
|
this.addAtom(Atom.create({
|
|
1329
1301
|
isotope: 'B',
|
|
1330
1302
|
wallet: this.sourceWallet,
|
|
1331
1303
|
value: -Number(this.sourceWallet.balance),
|
|
1332
|
-
meta: firstAtomMeta,
|
|
1333
1304
|
metaType: 'walletBundle',
|
|
1334
1305
|
metaId: this.sourceWallet.bundle!
|
|
1335
1306
|
}))
|
package/src/index.ts
CHANGED
|
@@ -403,7 +403,7 @@ export {
|
|
|
403
403
|
// MUST equal package.json's "version". The CI `version consistency` job enforces this via
|
|
404
404
|
// .github/scripts/check-version.sh, which parses this exact line — keep the literal form
|
|
405
405
|
// `export const SDK_VERSION = '<semver>'` intact so the gate can read it.
|
|
406
|
-
export const SDK_VERSION = '1.
|
|
406
|
+
export const SDK_VERSION = '1.3.0'
|
|
407
407
|
export const SDK_NAME = 'KnishIO-Client-TS'
|
|
408
408
|
export const COMPATIBLE_SERVER_VERSIONS = [4, 5]
|
|
409
409
|
|
|
@@ -70,7 +70,6 @@ import Wallet from '@/core/Wallet'
|
|
|
70
70
|
import Rule from '@/instance/rules/Rule'
|
|
71
71
|
import { base64ToHex, chunkSubstr } from '@/libraries/strings'
|
|
72
72
|
import { shake256 } from '@/libraries/crypto'
|
|
73
|
-
import Dot from '@/libraries/Dot'
|
|
74
73
|
import Molecule from '@/core/Molecule'
|
|
75
74
|
|
|
76
75
|
// Type definitions for Molecule structure
|
|
@@ -214,12 +213,13 @@ export default class CheckMolecule {
|
|
|
214
213
|
/**
|
|
215
214
|
* Validate isotope U atoms
|
|
216
215
|
* @return True if valid
|
|
217
|
-
* @throws WrongTokenTypeException if token is
|
|
216
|
+
* @throws WrongTokenTypeException if token is neither AUTH (fresh login) nor USER (re-login
|
|
217
|
+
* signed from the ContinuID pointer)
|
|
218
218
|
* @throws AtomIndexException if index is not 0
|
|
219
219
|
*/
|
|
220
220
|
isotopeU(): boolean {
|
|
221
221
|
for (const atom of this.molecule.getIsotopes('U')) {
|
|
222
|
-
if (atom.token !== 'AUTH') {
|
|
222
|
+
if (atom.token !== 'AUTH' && atom.token !== 'USER') {
|
|
223
223
|
throw new WrongTokenTypeException(`Check::isotopeU() - "${atom.token}" is not a valid Token slug for "${atom.isotope}" isotope Atoms!`)
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -747,22 +747,8 @@ export default class CheckMolecule {
|
|
|
747
747
|
throw new SignatureMismatchException()
|
|
748
748
|
}
|
|
749
749
|
|
|
750
|
-
//
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
// Get signing wallet from first atom's metas
|
|
754
|
-
const signingWallet = Dot.get(signingAtom.aggregatedMeta(), 'signingWallet')
|
|
755
|
-
|
|
756
|
-
// Try to get custom signing address from the metas (local molecule with server secret)
|
|
757
|
-
if (signingWallet) {
|
|
758
|
-
const parsedWallet = JSON.parse(signingWallet as string)
|
|
759
|
-
const walletAddress = Dot.get(parsedWallet, 'address')
|
|
760
|
-
if (walletAddress) {
|
|
761
|
-
signingAddress = walletAddress as string
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (address !== signingAddress) {
|
|
750
|
+
// The recovered address must match the address the signing atom claims
|
|
751
|
+
if (address !== signingAtom.walletAddress) {
|
|
766
752
|
throw new SignatureMismatchException()
|
|
767
753
|
}
|
|
768
754
|
|
|
@@ -47,7 +47,6 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import MutationProposeMolecule from './MutationProposeMolecule'
|
|
50
|
-
import type Wallet from '@/core/Wallet'
|
|
51
50
|
|
|
52
51
|
/**
|
|
53
52
|
* Mutation for withdrawing buffer tokens
|
|
@@ -57,15 +56,12 @@ export default class MutationWithdrawBufferToken extends MutationProposeMolecule
|
|
|
57
56
|
* Fills the molecule with buffer withdrawal data
|
|
58
57
|
*/
|
|
59
58
|
override fillMolecule({
|
|
60
|
-
recipients
|
|
61
|
-
signingWallet
|
|
59
|
+
recipients
|
|
62
60
|
}: {
|
|
63
61
|
recipients: Record<string, any>
|
|
64
|
-
signingWallet?: Wallet
|
|
65
62
|
}): void {
|
|
66
|
-
|
|
67
|
-
recipients
|
|
68
|
-
signingWallet
|
|
63
|
+
this.$__molecule.initWithdrawBuffer({
|
|
64
|
+
recipients
|
|
69
65
|
})
|
|
70
66
|
this.$__molecule.sign({})
|
|
71
67
|
this.$__molecule.check(this.$__molecule.sourceWallet)
|
|
@@ -64,8 +64,8 @@ export default class QueryContinuId extends Query {
|
|
|
64
64
|
constructor(graphQLClient: GraphQLClient, knishIOClient: KnishIOClient) {
|
|
65
65
|
super(graphQLClient, knishIOClient)
|
|
66
66
|
|
|
67
|
-
this.$__query = gql`query ($bundle: String
|
|
68
|
-
ContinuId(bundle: $bundle) {
|
|
67
|
+
this.$__query = gql`query ($bundle: String!, $token: String) {
|
|
68
|
+
ContinuId(bundle: $bundle, token: $token) {
|
|
69
69
|
address,
|
|
70
70
|
bundleHash,
|
|
71
71
|
tokenSlug,
|
|
@@ -188,6 +188,22 @@ export default class ResponseProposeMolecule extends Response {
|
|
|
188
188
|
code: 'VERIFICATION_FAILED'
|
|
189
189
|
})
|
|
190
190
|
}
|
|
191
|
+
// Validator 0.5.0 signer binding (Tier 1): atom 0's address is not the wallet registered at
|
|
192
|
+
// (bundle, token, position). Nothing was consumed.
|
|
193
|
+
if (/signer address mismatch/.test(lc)) {
|
|
194
|
+
return new SignatureMismatchException(reason, {
|
|
195
|
+
details: { reason },
|
|
196
|
+
code: 'SIGNER_ADDRESS_MISMATCH'
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
// Validator 0.5.0 (Tier 2): the signing key is consumed and the chain advanced, so the
|
|
200
|
+
// cached remainder is stale — handlePositionDrift keys on AtomIndexException.
|
|
201
|
+
if (/no accepted origin available/.test(lc)) {
|
|
202
|
+
return new AtomIndexException(reason, {
|
|
203
|
+
details: { reason },
|
|
204
|
+
code: 'ORIGIN_PENDING'
|
|
205
|
+
})
|
|
206
|
+
}
|
|
191
207
|
return null
|
|
192
208
|
}
|
|
193
209
|
|