@xyo-network/event-kit-protocol 0.1.1 → 0.1.3
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/README.md +21 -6
- package/dist/neutral/admissionCore.d.ts +30 -0
- package/dist/neutral/admissionCore.d.ts.map +1 -0
- package/dist/neutral/admissionTypes.d.ts +16 -3
- package/dist/neutral/admissionTypes.d.ts.map +1 -1
- package/dist/neutral/admitAndEnqueue.d.ts +6 -21
- package/dist/neutral/admitAndEnqueue.d.ts.map +1 -1
- package/dist/neutral/endpointChallenge.d.ts +2 -2
- package/dist/neutral/endpointChallenge.d.ts.map +1 -1
- package/dist/neutral/grantEvidence.d.ts +20 -0
- package/dist/neutral/grantEvidence.d.ts.map +1 -0
- package/dist/neutral/handshakeConstruction.d.ts +8 -4
- package/dist/neutral/handshakeConstruction.d.ts.map +1 -1
- package/dist/neutral/index.d.ts +4 -1
- package/dist/neutral/index.d.ts.map +1 -1
- package/dist/neutral/index.mjs +345 -167
- package/dist/neutral/index.mjs.map +4 -4
- package/dist/neutral/memoryStatementGrantGraph.d.ts.map +1 -1
- package/dist/neutral/memoryWakeInbox.d.ts +5 -5
- package/dist/neutral/memoryWakeInbox.d.ts.map +1 -1
- package/dist/neutral/memoryWakeQueue.d.ts +3 -2
- package/dist/neutral/memoryWakeQueue.d.ts.map +1 -1
- package/dist/neutral/publisherSubscription.d.ts.map +1 -1
- package/dist/neutral/sourceWakeActivation.d.ts +24 -0
- package/dist/neutral/sourceWakeActivation.d.ts.map +1 -0
- package/dist/neutral/sourceWakeIdentity.d.ts +6 -0
- package/dist/neutral/sourceWakeIdentity.d.ts.map +1 -0
- package/dist/neutral/statementGraphWakeGrantAuthorizer.d.ts +9 -11
- package/dist/neutral/statementGraphWakeGrantAuthorizer.d.ts.map +1 -1
- package/dist/neutral/types.d.ts +41 -19
- package/dist/neutral/types.d.ts.map +1 -1
- package/dist/neutral/verifiedAdmissionWake.d.ts +22 -0
- package/dist/neutral/verifiedAdmissionWake.d.ts.map +1 -0
- package/dist/neutral/verifyWakeRequest.d.ts +41 -30
- package/dist/neutral/verifyWakeRequest.d.ts.map +1 -1
- package/dist/neutral/wakeQueue.d.ts +7 -7
- package/dist/neutral/wakeQueue.d.ts.map +1 -1
- package/dist/neutral/wakeSubscriptionHpke.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/neutral/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// src/admissionTypes.ts
|
|
2
2
|
var EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS = 60;
|
|
3
3
|
|
|
4
|
-
// src/
|
|
5
|
-
async function
|
|
4
|
+
// src/admissionCore.ts
|
|
5
|
+
async function admitVerifiedWake(input) {
|
|
6
6
|
const attemptResult = createAttempt(input.verified);
|
|
7
7
|
if (!attemptResult.ok) return attemptResult.failure;
|
|
8
8
|
const { attempt } = attemptResult;
|
|
9
|
-
const wake =
|
|
9
|
+
const { wake } = input;
|
|
10
10
|
const prior = await resolvePriorAttempt(input.inbox, attempt, wake);
|
|
11
11
|
if (!prior.pending) return prior.result;
|
|
12
12
|
if (!isNonnegativeSafeInteger(input.expectedHead)) {
|
|
@@ -90,6 +90,10 @@ async function authorize(input) {
|
|
|
90
90
|
try {
|
|
91
91
|
decision = await input.grants.authorizePublisher({
|
|
92
92
|
controller: route.controller,
|
|
93
|
+
authorizationChainId: route.authorizationChainId,
|
|
94
|
+
sourceHash: route.sourceHash,
|
|
95
|
+
filterHash: route.filterHash,
|
|
96
|
+
subscriptionHash: route.subscriptionHash,
|
|
93
97
|
deploymentId: route.deploymentId,
|
|
94
98
|
expectedHead: input.expectedHead,
|
|
95
99
|
publisher: input.verified.publisher,
|
|
@@ -137,6 +141,10 @@ function validateGrantEvidence(evidence, verified, atBlock) {
|
|
|
137
141
|
if (!isRecord(evidence)) return reject("grant-view-invalid", ["enabled grant decision omitted evidence"]);
|
|
138
142
|
const requiredFields = [
|
|
139
143
|
"assertedAtBlock",
|
|
144
|
+
"authorizationChainId",
|
|
145
|
+
"filterHash",
|
|
146
|
+
"sourceHash",
|
|
147
|
+
"subscriptionHash",
|
|
140
148
|
"controller",
|
|
141
149
|
"deploymentId",
|
|
142
150
|
"id",
|
|
@@ -154,6 +162,9 @@ function validateGrantEvidence(evidence, verified, atBlock) {
|
|
|
154
162
|
if (evidence.controller !== verified.route.controller) {
|
|
155
163
|
return reject("grant-view-invalid", ["grant controller does not match the configured controller"]);
|
|
156
164
|
}
|
|
165
|
+
if (evidence.authorizationChainId !== verified.route.authorizationChainId || evidence.sourceHash !== verified.route.sourceHash || evidence.filterHash !== verified.route.filterHash || evidence.subscriptionHash !== verified.route.subscriptionHash) {
|
|
166
|
+
return reject("grant-view-invalid", ["grant authorization chain or source does not match the configured route"]);
|
|
167
|
+
}
|
|
157
168
|
if (evidence.target !== verified.publisher) {
|
|
158
169
|
return reject("grant-view-invalid", ["grant target does not match the verified publisher"]);
|
|
159
170
|
}
|
|
@@ -183,6 +194,11 @@ function validateReceipt(receipt, attempt) {
|
|
|
183
194
|
function snapshotGrantEvidence(evidence) {
|
|
184
195
|
return Object.freeze({
|
|
185
196
|
assertedAtBlock: evidence.assertedAtBlock,
|
|
197
|
+
authorizationChainId: evidence.authorizationChainId,
|
|
198
|
+
sourceHash: evidence.sourceHash,
|
|
199
|
+
filterHash: evidence.filterHash,
|
|
200
|
+
subscriptionHash: evidence.subscriptionHash,
|
|
201
|
+
...evidence.objectHash === void 0 ? {} : { objectHash: evidence.objectHash },
|
|
186
202
|
controller: evidence.controller,
|
|
187
203
|
deploymentId: evidence.deploymentId,
|
|
188
204
|
id: evidence.id,
|
|
@@ -199,20 +215,6 @@ function snapshotReceipt(receipt) {
|
|
|
199
215
|
wakeId: receipt.wakeId
|
|
200
216
|
});
|
|
201
217
|
}
|
|
202
|
-
function toAdmittedWake(verified) {
|
|
203
|
-
return Object.freeze({
|
|
204
|
-
audience: verified.audience,
|
|
205
|
-
deploymentId: verified.deploymentId,
|
|
206
|
-
event: verified.event,
|
|
207
|
-
eventHash: verified.eventHash,
|
|
208
|
-
fromBlock: verified.fromBlock,
|
|
209
|
-
jti: verified.jwtId,
|
|
210
|
-
publisher: verified.publisher,
|
|
211
|
-
...verified.queueId === void 0 ? {} : { queueId: verified.queueId },
|
|
212
|
-
toBlock: verified.toBlock,
|
|
213
|
-
wakeId: verified.wakeId
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
218
|
function success(disposition, receipt, wake) {
|
|
217
219
|
return Object.freeze({
|
|
218
220
|
disposition,
|
|
@@ -239,6 +241,60 @@ function errorMessage(error) {
|
|
|
239
241
|
return error instanceof Error ? error.message : String(error);
|
|
240
242
|
}
|
|
241
243
|
|
|
244
|
+
// src/types.ts
|
|
245
|
+
import {
|
|
246
|
+
DeploymentIdZod,
|
|
247
|
+
HashZod,
|
|
248
|
+
NonnegativeJsonSafeIntegerZod,
|
|
249
|
+
NormalizedAddressZod,
|
|
250
|
+
WakeEnvelopeZod,
|
|
251
|
+
WakeIdZod,
|
|
252
|
+
WakeJwtAudienceZod,
|
|
253
|
+
WakeJwtIdZod
|
|
254
|
+
} from "@xyo-network/event-kit-schemas";
|
|
255
|
+
import { PayloadBuilder } from "@xyo-network/sdk";
|
|
256
|
+
import { z } from "zod";
|
|
257
|
+
var AdmittedWakeZod = z.strictObject({
|
|
258
|
+
audience: WakeJwtAudienceZod,
|
|
259
|
+
deploymentId: DeploymentIdZod,
|
|
260
|
+
event: WakeEnvelopeZod,
|
|
261
|
+
eventHash: HashZod,
|
|
262
|
+
fromPosition: NonnegativeJsonSafeIntegerZod,
|
|
263
|
+
toPosition: NonnegativeJsonSafeIntegerZod,
|
|
264
|
+
jti: WakeJwtIdZod,
|
|
265
|
+
publisher: NormalizedAddressZod,
|
|
266
|
+
queueId: z.string().min(1).max(255).optional(),
|
|
267
|
+
wakeId: WakeIdZod
|
|
268
|
+
}).refine(
|
|
269
|
+
(wake) => wake.deploymentId === wake.event.deploymentId && wake.queueId === wake.event.queueId && wake.wakeId === wake.event.wakeId && wake.fromPosition === wake.event.fromPosition && wake.toPosition === wake.event.toPosition,
|
|
270
|
+
"Persisted admission metadata must exactly match its signed envelope"
|
|
271
|
+
);
|
|
272
|
+
async function parseAdmittedWake(value) {
|
|
273
|
+
const wake = AdmittedWakeZod.parse(value);
|
|
274
|
+
if (await PayloadBuilder.dataHash(wake.event) !== wake.eventHash) {
|
|
275
|
+
throw new TypeError("Persisted wake content does not match its admitted event hash");
|
|
276
|
+
}
|
|
277
|
+
return wake;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/admitAndEnqueue.ts
|
|
281
|
+
async function admitAndEnqueue(input) {
|
|
282
|
+
const { verified } = input;
|
|
283
|
+
const wake = AdmittedWakeZod.parse({
|
|
284
|
+
audience: verified.audience,
|
|
285
|
+
deploymentId: verified.deploymentId,
|
|
286
|
+
event: verified.event,
|
|
287
|
+
eventHash: verified.eventHash,
|
|
288
|
+
fromPosition: verified.fromPosition,
|
|
289
|
+
toPosition: verified.toPosition,
|
|
290
|
+
jti: verified.jwtId,
|
|
291
|
+
publisher: verified.publisher,
|
|
292
|
+
...verified.queueId === void 0 ? {} : { queueId: verified.queueId },
|
|
293
|
+
wakeId: verified.wakeId
|
|
294
|
+
});
|
|
295
|
+
return await admitVerifiedWake({ ...input, wake });
|
|
296
|
+
}
|
|
297
|
+
|
|
242
298
|
// src/canonicalJson.ts
|
|
243
299
|
var textEncoder = new TextEncoder();
|
|
244
300
|
function hasUnpairedSurrogate(value) {
|
|
@@ -301,7 +357,7 @@ import {
|
|
|
301
357
|
generateHandshakeId,
|
|
302
358
|
MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS
|
|
303
359
|
} from "@xyo-network/event-kit-schemas";
|
|
304
|
-
import { PayloadBuilder } from "@xyo-network/sdk";
|
|
360
|
+
import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
|
|
305
361
|
function hashBytes(hash) {
|
|
306
362
|
return Uint8Array.from({ length: 32 }, (_, index) => Number.parseInt(hash.slice(index * 2, index * 2 + 2), 16));
|
|
307
363
|
}
|
|
@@ -328,7 +384,7 @@ function equalProof(left, right) {
|
|
|
328
384
|
}
|
|
329
385
|
async function endpointChallengeHash(challenge) {
|
|
330
386
|
const parsed = EndpointChallengeZod.parse(challenge);
|
|
331
|
-
return await
|
|
387
|
+
return await PayloadBuilder2.dataHash(parsed);
|
|
332
388
|
}
|
|
333
389
|
function createEndpointChallenge(input) {
|
|
334
390
|
const lifetimeSeconds = input.lifetimeSeconds ?? MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS;
|
|
@@ -341,7 +397,7 @@ function createEndpointChallenge(input) {
|
|
|
341
397
|
if (input.issuedAt > Number.MAX_SAFE_INTEGER - lifetimeSeconds) {
|
|
342
398
|
throw new RangeError("Challenge expiry exceeds the JSON safe-integer range");
|
|
343
399
|
}
|
|
344
|
-
return EndpointChallengeZod.parse(new
|
|
400
|
+
return EndpointChallengeZod.parse(new PayloadBuilder2({ schema: EndpointChallengeSchema }).fields({
|
|
345
401
|
challengeId: generateHandshakeId(),
|
|
346
402
|
chainId: input.chainId,
|
|
347
403
|
publisher: input.publisher,
|
|
@@ -359,7 +415,7 @@ function createEndpointChallenge(input) {
|
|
|
359
415
|
}
|
|
360
416
|
async function createEndpointChallengeResponse(challenge, secret) {
|
|
361
417
|
const challengeHash = await endpointChallengeHash(challenge);
|
|
362
|
-
return EndpointChallengeResponseZod.parse(new
|
|
418
|
+
return EndpointChallengeResponseZod.parse(new PayloadBuilder2({ schema: EndpointChallengeResponseSchema }).fields({
|
|
363
419
|
challenge: challengeHash,
|
|
364
420
|
proof: await hmacProof(secret, challengeHash)
|
|
365
421
|
}).build());
|
|
@@ -393,11 +449,9 @@ import {
|
|
|
393
449
|
ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
|
|
394
450
|
EndpointChallengeResponseZod as EndpointChallengeResponseZod2,
|
|
395
451
|
EndpointChallengeZod as EndpointChallengeZod2,
|
|
396
|
-
FINALIZED_HEAD_EVENT_KIND,
|
|
397
|
-
FINALIZED_HEAD_FILTER_PROFILE,
|
|
398
452
|
generateGrantId,
|
|
399
453
|
generateHandshakeId as generateHandshakeId2,
|
|
400
|
-
HashZod as
|
|
454
|
+
HashZod as HashZod3,
|
|
401
455
|
MAX_SUBSCRIPTION_PLAINTEXT_BYTES as MAX_SUBSCRIPTION_PLAINTEXT_BYTES2,
|
|
402
456
|
PublisherEncryptionKeyRevocationSchema,
|
|
403
457
|
PublisherEncryptionKeyRevocationZod,
|
|
@@ -407,6 +461,8 @@ import {
|
|
|
407
461
|
PublisherServiceRevocationZod,
|
|
408
462
|
PublisherServiceSchema,
|
|
409
463
|
PublisherServiceZod,
|
|
464
|
+
SOURCE_WAKE_EVENT_KIND,
|
|
465
|
+
SOURCE_WAKE_FILTER_PROFILE,
|
|
410
466
|
WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
411
467
|
WAKE_WALLET_JWT_DELIVERY_PROFILE,
|
|
412
468
|
WakeGrantObjectSchema,
|
|
@@ -423,7 +479,7 @@ import {
|
|
|
423
479
|
WakeSubscriptionRevocationSchema,
|
|
424
480
|
WakeSubscriptionRevocationZod
|
|
425
481
|
} from "@xyo-network/event-kit-schemas";
|
|
426
|
-
import { PayloadBuilder as
|
|
482
|
+
import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/sdk";
|
|
427
483
|
|
|
428
484
|
// src/wakeSubscriptionHpke.ts
|
|
429
485
|
import {
|
|
@@ -438,8 +494,8 @@ import {
|
|
|
438
494
|
canonicalBase64UrlToBytes as canonicalBase64UrlToBytes2,
|
|
439
495
|
EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod2,
|
|
440
496
|
HandshakeIdZod,
|
|
441
|
-
HashZod,
|
|
442
|
-
NormalizedAddressZod as
|
|
497
|
+
HashZod as HashZod2,
|
|
498
|
+
NormalizedAddressZod as NormalizedAddressZod3,
|
|
443
499
|
WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE2,
|
|
444
500
|
WakeSubscriptionCleartextZod as WakeSubscriptionCleartextZod2
|
|
445
501
|
} from "@xyo-network/event-kit-schemas";
|
|
@@ -448,7 +504,7 @@ import {
|
|
|
448
504
|
import {
|
|
449
505
|
EncryptedWakeSubscriptionZod,
|
|
450
506
|
MAX_SUBSCRIPTION_PLAINTEXT_BYTES,
|
|
451
|
-
NormalizedAddressZod,
|
|
507
|
+
NormalizedAddressZod as NormalizedAddressZod2,
|
|
452
508
|
WAKE_SUBSCRIPTION_HPKE_PROFILE,
|
|
453
509
|
WakeSubscriptionCleartextZod,
|
|
454
510
|
WakeSubscriptionPlaintextZod
|
|
@@ -465,7 +521,7 @@ function cleartextFromEncrypted(subscription) {
|
|
|
465
521
|
return cleartext;
|
|
466
522
|
}
|
|
467
523
|
function wakeSubscriptionAadBytes(subscription, subscriber) {
|
|
468
|
-
const parsedSubscriber =
|
|
524
|
+
const parsedSubscriber = NormalizedAddressZod2.parse(subscriber);
|
|
469
525
|
const parsedSubscription = "encryption" in subscription ? cleartextFromEncrypted(EncryptedWakeSubscriptionZod.parse(subscription)) : WakeSubscriptionCleartextZod.parse(subscription);
|
|
470
526
|
return canonicalJsonBytes({
|
|
471
527
|
schema: parsedSubscription.schema,
|
|
@@ -528,14 +584,14 @@ var WakeSubscriptionDecryptionError = class extends Error {
|
|
|
528
584
|
};
|
|
529
585
|
function parsePublicKey(value) {
|
|
530
586
|
return {
|
|
531
|
-
encryptionKey:
|
|
587
|
+
encryptionKey: HashZod2.parse(value.encryptionKey),
|
|
532
588
|
keyId: HandshakeIdZod.parse(value.keyId),
|
|
533
589
|
publicKey: Base64Url32BytesZod2.parse(value.publicKey)
|
|
534
590
|
};
|
|
535
591
|
}
|
|
536
592
|
function parsePrivateKey(value) {
|
|
537
593
|
return {
|
|
538
|
-
encryptionKey:
|
|
594
|
+
encryptionKey: HashZod2.parse(value.encryptionKey),
|
|
539
595
|
keyId: HandshakeIdZod.parse(value.keyId),
|
|
540
596
|
privateKey: Base64Url32BytesZod2.parse(value.privateKey)
|
|
541
597
|
};
|
|
@@ -559,7 +615,7 @@ async function generateWakeSubscriptionEncryptionKeyPair() {
|
|
|
559
615
|
}
|
|
560
616
|
async function encryptWakeSubscription(input) {
|
|
561
617
|
const subscription = WakeSubscriptionCleartextZod2.parse(input.subscription);
|
|
562
|
-
const subscriber =
|
|
618
|
+
const subscriber = NormalizedAddressZod3.parse(input.subscriber);
|
|
563
619
|
const publisherKey = parsePublicKey(input.publisherKey);
|
|
564
620
|
if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) {
|
|
565
621
|
throw new TypeError("Subscription key reference does not match the supplied publisher key");
|
|
@@ -582,7 +638,7 @@ async function encryptWakeSubscription(input) {
|
|
|
582
638
|
async function decryptWakeSubscription(input) {
|
|
583
639
|
try {
|
|
584
640
|
const subscription = EncryptedWakeSubscriptionZod2.parse(input.subscription);
|
|
585
|
-
const subscriber =
|
|
641
|
+
const subscriber = NormalizedAddressZod3.parse(input.subscriber);
|
|
586
642
|
const publisherKey = parsePrivateKey(input.publisherKey);
|
|
587
643
|
if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) throw new TypeError("Key reference mismatch");
|
|
588
644
|
const recipientKey = await suite.kem.deserializePrivateKey(decoded32(publisherKey.privateKey));
|
|
@@ -595,7 +651,8 @@ async function decryptWakeSubscription(input) {
|
|
|
595
651
|
canonicalBase64UrlToBytes2(subscription.encryption.ciphertext) ?? new Uint8Array(),
|
|
596
652
|
wakeSubscriptionAadBytes(subscription, subscriber)
|
|
597
653
|
);
|
|
598
|
-
|
|
654
|
+
const parsed = wakeSubscriptionPlaintextFromBytes(new Uint8Array(plaintext));
|
|
655
|
+
return parsed;
|
|
599
656
|
} catch {
|
|
600
657
|
throw new WakeSubscriptionDecryptionError();
|
|
601
658
|
}
|
|
@@ -624,13 +681,13 @@ function parseHandshakePublicObject(value) {
|
|
|
624
681
|
throw new TypeError("Expected a strict Event Kit handshake public object");
|
|
625
682
|
}
|
|
626
683
|
async function handshakeObjectHash(object) {
|
|
627
|
-
return
|
|
684
|
+
return HashZod3.parse(await PayloadBuilder3.dataHash(parseHandshakePublicObject(object)));
|
|
628
685
|
}
|
|
629
686
|
async function withHandshakeObjectHash(body) {
|
|
630
687
|
return Object.freeze({ body, objectHash: await handshakeObjectHash(body) });
|
|
631
688
|
}
|
|
632
689
|
function createPublisherEncryptionKey(input) {
|
|
633
|
-
return PublisherEncryptionKeyZod.parse(new
|
|
690
|
+
return PublisherEncryptionKeyZod.parse(new PayloadBuilder3({ schema: PublisherEncryptionKeySchema }).fields({
|
|
634
691
|
keyId: input.keyId ?? generateHandshakeId2(),
|
|
635
692
|
profile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
636
693
|
publicKey: input.publicKey,
|
|
@@ -661,20 +718,25 @@ async function generatePublisherEncryptionKey(input) {
|
|
|
661
718
|
});
|
|
662
719
|
}
|
|
663
720
|
function createPublisherService(input) {
|
|
664
|
-
return PublisherServiceZod.parse(new
|
|
721
|
+
return PublisherServiceZod.parse(new PayloadBuilder3({ schema: PublisherServiceSchema }).fields({
|
|
722
|
+
authorizationChainId: input.authorizationChainId,
|
|
665
723
|
serviceId: input.serviceId ?? generateHandshakeId2(),
|
|
666
724
|
revision: input.revision ?? 1,
|
|
667
725
|
...input.supersedes === void 0 ? {} : { supersedes: input.supersedes },
|
|
668
726
|
validFromBlock: input.validFromBlock,
|
|
669
727
|
...input.expiresAtBlock === void 0 ? {} : { expiresAtBlock: input.expiresAtBlock },
|
|
670
|
-
events:
|
|
728
|
+
events: input.sources.map((source) => ({
|
|
729
|
+
kind: SOURCE_WAKE_EVENT_KIND,
|
|
730
|
+
filterProfile: SOURCE_WAKE_FILTER_PROFILE,
|
|
731
|
+
source
|
|
732
|
+
})),
|
|
671
733
|
deliveryProfile: WAKE_WALLET_JWT_DELIVERY_PROFILE,
|
|
672
734
|
challengeProfile: ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
|
|
673
735
|
encryptionProfile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
674
736
|
encryptionKeys: [...input.encryptionKeys],
|
|
675
737
|
preferredEncryptionKey: input.preferredEncryptionKey,
|
|
676
738
|
limits: {
|
|
677
|
-
|
|
739
|
+
maxPositionsPerWake: input.maxPositionsPerWake,
|
|
678
740
|
maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks,
|
|
679
741
|
maxPlaintextBytes: MAX_SUBSCRIPTION_PLAINTEXT_BYTES2
|
|
680
742
|
},
|
|
@@ -682,7 +744,7 @@ function createPublisherService(input) {
|
|
|
682
744
|
}).build());
|
|
683
745
|
}
|
|
684
746
|
function createWakeGrant(input) {
|
|
685
|
-
return WakeGrantObjectZod.parse(new
|
|
747
|
+
return WakeGrantObjectZod.parse(new PayloadBuilder3({ schema: WakeGrantObjectSchema }).fields({
|
|
686
748
|
grantId: input.grantId ?? generateGrantId(),
|
|
687
749
|
deploymentId: input.deploymentId,
|
|
688
750
|
publisher: input.publisher,
|
|
@@ -696,14 +758,16 @@ function createWakeSubscriptionPlaintext(input) {
|
|
|
696
758
|
globalThis.crypto.getRandomValues(secretBytes);
|
|
697
759
|
}
|
|
698
760
|
const challengeSecret = input.challengeSecret ?? bytesToCanonicalBase64Url3(secretBytes);
|
|
699
|
-
return WakeSubscriptionPlaintextZod2.parse(new
|
|
761
|
+
return WakeSubscriptionPlaintextZod2.parse(new PayloadBuilder3({ schema: WakeSubscriptionPlaintextSchema }).fields({
|
|
700
762
|
endpoint: input.endpoint,
|
|
701
763
|
audience: input.audience,
|
|
702
764
|
deploymentId: input.deploymentId,
|
|
703
765
|
queueId: input.queueId ?? null,
|
|
704
766
|
filter: {
|
|
705
|
-
schema:
|
|
706
|
-
|
|
767
|
+
schema: SOURCE_WAKE_FILTER_PROFILE,
|
|
768
|
+
source: input.source,
|
|
769
|
+
fromPosition: input.fromPosition,
|
|
770
|
+
maxPositionsPerWake: input.maxPositionsPerWake
|
|
707
771
|
},
|
|
708
772
|
challenge: {
|
|
709
773
|
profile: ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
|
|
@@ -713,7 +777,7 @@ function createWakeSubscriptionPlaintext(input) {
|
|
|
713
777
|
}
|
|
714
778
|
async function createEncryptedWakeSubscription(input) {
|
|
715
779
|
const cleartext = WakeSubscriptionCleartextZod3.parse(
|
|
716
|
-
new
|
|
780
|
+
new PayloadBuilder3({ schema: EncryptedWakeSubscriptionSchema }).fields({
|
|
717
781
|
subscriptionId: input.subscriptionId ?? generateHandshakeId2(),
|
|
718
782
|
revision: input.revision ?? 1,
|
|
719
783
|
...input.supersedes === void 0 ? {} : { supersedes: input.supersedes },
|
|
@@ -723,7 +787,7 @@ async function createEncryptedWakeSubscription(input) {
|
|
|
723
787
|
encryptionKey: input.publisherKey.encryptionKey,
|
|
724
788
|
keyId: input.publisherKey.keyId,
|
|
725
789
|
grant: input.grant,
|
|
726
|
-
eventKind:
|
|
790
|
+
eventKind: SOURCE_WAKE_EVENT_KIND,
|
|
727
791
|
expiresAtBlock: input.expiresAtBlock
|
|
728
792
|
}).build()
|
|
729
793
|
);
|
|
@@ -735,7 +799,7 @@ async function createEncryptedWakeSubscription(input) {
|
|
|
735
799
|
});
|
|
736
800
|
}
|
|
737
801
|
function createWakeSubscriptionAcceptance(input) {
|
|
738
|
-
return WakeSubscriptionAcceptanceZod.parse(new
|
|
802
|
+
return WakeSubscriptionAcceptanceZod.parse(new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceSchema }).fields({
|
|
739
803
|
challenge: input.challenge,
|
|
740
804
|
encryptionKey: input.encryptionKey,
|
|
741
805
|
grant: input.grant,
|
|
@@ -745,22 +809,22 @@ function createWakeSubscriptionAcceptance(input) {
|
|
|
745
809
|
}).build());
|
|
746
810
|
}
|
|
747
811
|
function createPublisherServiceRevocation(service) {
|
|
748
|
-
return PublisherServiceRevocationZod.parse(new
|
|
812
|
+
return PublisherServiceRevocationZod.parse(new PayloadBuilder3({ schema: PublisherServiceRevocationSchema }).fields({ service }).build());
|
|
749
813
|
}
|
|
750
814
|
function createPublisherEncryptionKeyRevocation(key) {
|
|
751
815
|
return PublisherEncryptionKeyRevocationZod.parse(
|
|
752
|
-
new
|
|
816
|
+
new PayloadBuilder3({ schema: PublisherEncryptionKeyRevocationSchema }).fields({ key }).build()
|
|
753
817
|
);
|
|
754
818
|
}
|
|
755
819
|
function createWakeGrantRevocation(grant) {
|
|
756
|
-
return WakeGrantRevocationZod.parse(new
|
|
820
|
+
return WakeGrantRevocationZod.parse(new PayloadBuilder3({ schema: WakeGrantRevocationSchema }).fields({ grant }).build());
|
|
757
821
|
}
|
|
758
822
|
function createWakeSubscriptionRevocation(subscription) {
|
|
759
|
-
return WakeSubscriptionRevocationZod.parse(new
|
|
823
|
+
return WakeSubscriptionRevocationZod.parse(new PayloadBuilder3({ schema: WakeSubscriptionRevocationSchema }).fields({ subscription }).build());
|
|
760
824
|
}
|
|
761
825
|
function createWakeSubscriptionAcceptanceRevocation(acceptance) {
|
|
762
826
|
return WakeSubscriptionAcceptanceRevocationZod.parse(
|
|
763
|
-
new
|
|
827
|
+
new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceRevocationSchema }).fields({ acceptance }).build()
|
|
764
828
|
);
|
|
765
829
|
}
|
|
766
830
|
|
|
@@ -1161,12 +1225,36 @@ import {
|
|
|
1161
1225
|
ChainIdZod,
|
|
1162
1226
|
EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod4,
|
|
1163
1227
|
EndpointChallengeZod as EndpointChallengeZod3,
|
|
1164
|
-
HashZod as
|
|
1165
|
-
|
|
1228
|
+
HashZod as HashZod4,
|
|
1229
|
+
hasMatchingHandshakeProfile,
|
|
1230
|
+
NormalizedAddressZod as NormalizedAddressZod4,
|
|
1166
1231
|
PublisherEncryptionKeyZod as PublisherEncryptionKeyZod2,
|
|
1167
1232
|
PublisherServiceZod as PublisherServiceZod2,
|
|
1168
1233
|
WakeGrantObjectZod as WakeGrantObjectZod2
|
|
1169
1234
|
} from "@xyo-network/event-kit-schemas";
|
|
1235
|
+
|
|
1236
|
+
// src/sourceWakeIdentity.ts
|
|
1237
|
+
import {
|
|
1238
|
+
SourceWakeSubscriptionFilterZod,
|
|
1239
|
+
WakeSourceSchema,
|
|
1240
|
+
WakeSourceZod
|
|
1241
|
+
} from "@xyo-network/event-kit-schemas";
|
|
1242
|
+
import {
|
|
1243
|
+
asSchema,
|
|
1244
|
+
PayloadBuilder as PayloadBuilder4
|
|
1245
|
+
} from "@xyo-network/sdk";
|
|
1246
|
+
async function wakeSourceHash(source) {
|
|
1247
|
+
return await PayloadBuilder4.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
|
|
1248
|
+
}
|
|
1249
|
+
function wakeSourcesEqual(left, right) {
|
|
1250
|
+
return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
|
|
1251
|
+
}
|
|
1252
|
+
async function sourceWakeFilterHash(filter) {
|
|
1253
|
+
const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
|
|
1254
|
+
return await PayloadBuilder4.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// src/publisherSubscription.ts
|
|
1170
1258
|
function rejectPublisherSubscription(reason) {
|
|
1171
1259
|
return Object.freeze({ ok: false, reason });
|
|
1172
1260
|
}
|
|
@@ -1201,12 +1289,12 @@ function parsePublisherSubscriptionCandidate(candidate) {
|
|
|
1201
1289
|
chainId: ChainIdZod.parse(candidate.chainId),
|
|
1202
1290
|
grant: WakeGrantObjectZod2.parse(candidate.grant.body),
|
|
1203
1291
|
key: PublisherEncryptionKeyZod2.parse(candidate.key.body),
|
|
1204
|
-
publisher:
|
|
1292
|
+
publisher: NormalizedAddressZod4.parse(candidate.publisher),
|
|
1205
1293
|
service: PublisherServiceZod2.parse(candidate.service.body),
|
|
1206
|
-
subscriber:
|
|
1294
|
+
subscriber: NormalizedAddressZod4.parse(candidate.subscriber),
|
|
1207
1295
|
subscription: EncryptedWakeSubscriptionZod4.parse(candidate.subscription.body),
|
|
1208
|
-
tupleObjectHashes: candidate.tupleObjectHashes.map((hash) =>
|
|
1209
|
-
tupleSubject:
|
|
1296
|
+
tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod4.parse(hash)),
|
|
1297
|
+
tupleSubject: NormalizedAddressZod4.parse(candidate.tupleSubject)
|
|
1210
1298
|
};
|
|
1211
1299
|
} catch {
|
|
1212
1300
|
return void 0;
|
|
@@ -1225,6 +1313,7 @@ async function bodyHashesAreValid(candidate, parsed) {
|
|
|
1225
1313
|
return canonicalHashes[0] === candidate.service.objectHash && canonicalHashes[1] === candidate.key.objectHash && canonicalHashes[2] === candidate.grant.objectHash && canonicalHashes[3] === candidate.subscription.objectHash;
|
|
1226
1314
|
}
|
|
1227
1315
|
function publicBindingFailure(candidate, parsed) {
|
|
1316
|
+
if (!hasMatchingHandshakeProfile(parsed.service, parsed.subscription, parsed.chainId)) return "subscription-binding-invalid";
|
|
1228
1317
|
if (!hasExactGrantBinding(parsed.grant, parsed.publisher)) return "grant-binding-invalid";
|
|
1229
1318
|
if (!hasExactSubscriptionBinding(candidate, parsed.subscription)) return "subscription-binding-invalid";
|
|
1230
1319
|
const hashedKey = { body: parsed.key, objectHash: candidate.key.objectHash };
|
|
@@ -1296,7 +1385,7 @@ async function validateAndDecryptPublisherSubscription(candidate) {
|
|
|
1296
1385
|
if (!hasExactQueueScope(parsed.grant, decrypted.plaintext)) {
|
|
1297
1386
|
return rejectPublisherSubscription("grant-scope-invalid");
|
|
1298
1387
|
}
|
|
1299
|
-
if (
|
|
1388
|
+
if (!filterWithinServicePolicy(parsed.service, decrypted.plaintext)) {
|
|
1300
1389
|
return rejectPublisherSubscription("service-limit-invalid");
|
|
1301
1390
|
}
|
|
1302
1391
|
return validatedPublisherSubscription(candidate, parsed, decrypted.plaintext);
|
|
@@ -1311,11 +1400,13 @@ async function challengeRecordMatches(record, challenge, challengeHash, response
|
|
|
1311
1400
|
return responseHash === void 0 || record.responseHash === responseHash;
|
|
1312
1401
|
}
|
|
1313
1402
|
async function issuePublisherEndpointChallenge(input) {
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1403
|
+
const context = input.subscription.context;
|
|
1404
|
+
const common = {
|
|
1405
|
+
...context,
|
|
1316
1406
|
issuedAt: input.issuedAt,
|
|
1317
1407
|
...input.lifetimeSeconds === void 0 ? {} : { lifetimeSeconds: input.lifetimeSeconds }
|
|
1318
|
-
}
|
|
1408
|
+
};
|
|
1409
|
+
const challenge = createEndpointChallenge(common);
|
|
1319
1410
|
const challengeHash = await endpointChallengeHash(challenge);
|
|
1320
1411
|
try {
|
|
1321
1412
|
const result = await input.store.issue({ challenge, challengeHash });
|
|
@@ -1337,7 +1428,7 @@ async function beginPublisherEndpointChallengeAttempt(input) {
|
|
|
1337
1428
|
if (!isNonnegativeSafeInteger3(input.nowSeconds)) return { kind: "store-invalid" };
|
|
1338
1429
|
try {
|
|
1339
1430
|
const result = await input.store.beginAttempt({
|
|
1340
|
-
challengeHash:
|
|
1431
|
+
challengeHash: HashZod4.parse(input.challengeHash),
|
|
1341
1432
|
nowSeconds: input.nowSeconds
|
|
1342
1433
|
});
|
|
1343
1434
|
if (!["already-attempted", "expired", "missing", "started", "verified"].includes(result.kind)) {
|
|
@@ -1346,7 +1437,7 @@ async function beginPublisherEndpointChallengeAttempt(input) {
|
|
|
1346
1437
|
if (result.kind !== "missing" && !await challengeRecordMatches(
|
|
1347
1438
|
result.record,
|
|
1348
1439
|
result.record.challenge,
|
|
1349
|
-
|
|
1440
|
+
HashZod4.parse(input.challengeHash)
|
|
1350
1441
|
)) return { kind: "store-invalid" };
|
|
1351
1442
|
if ((result.kind === "started" || result.kind === "already-attempted") && result.record.state !== "attempted") return { kind: "store-invalid" };
|
|
1352
1443
|
if (result.kind === "verified" && result.record.state !== "verified") return { kind: "store-invalid" };
|
|
@@ -1424,18 +1515,57 @@ async function completePublisherEndpointChallenge(input) {
|
|
|
1424
1515
|
return completeFailure("challenge-store-unavailable");
|
|
1425
1516
|
}
|
|
1426
1517
|
}
|
|
1518
|
+
function filterWithinServicePolicy(service, plaintext) {
|
|
1519
|
+
const filter = plaintext.filter;
|
|
1520
|
+
return filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// src/sourceWakeActivation.ts
|
|
1524
|
+
import {
|
|
1525
|
+
ChainIdZod as ChainIdZod2,
|
|
1526
|
+
DeploymentIdZod as DeploymentIdZod2,
|
|
1527
|
+
HashZod as HashZod5,
|
|
1528
|
+
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
|
|
1529
|
+
NormalizedAddressZod as NormalizedAddressZod5,
|
|
1530
|
+
PositiveJsonSafeIntegerZod
|
|
1531
|
+
} from "@xyo-network/event-kit-schemas";
|
|
1532
|
+
import { z as z2 } from "zod";
|
|
1533
|
+
var SourceWakeSubscriptionActivationZod = z2.strictObject({
|
|
1534
|
+
atBlock: NonnegativeJsonSafeIntegerZod2,
|
|
1535
|
+
activeFromBlock: NonnegativeJsonSafeIntegerZod2,
|
|
1536
|
+
expiresAtBlock: PositiveJsonSafeIntegerZod,
|
|
1537
|
+
authorizationChainId: ChainIdZod2,
|
|
1538
|
+
controller: NormalizedAddressZod5,
|
|
1539
|
+
publisher: NormalizedAddressZod5,
|
|
1540
|
+
deploymentId: DeploymentIdZod2,
|
|
1541
|
+
queueId: z2.string().min(1).max(255).optional(),
|
|
1542
|
+
sourceHash: HashZod5,
|
|
1543
|
+
filterHash: HashZod5,
|
|
1544
|
+
subscriptionHash: HashZod5,
|
|
1545
|
+
serviceHash: HashZod5,
|
|
1546
|
+
grantHash: HashZod5
|
|
1547
|
+
}).refine(
|
|
1548
|
+
(value) => value.activeFromBlock <= value.atBlock && value.atBlock < value.expiresAtBlock,
|
|
1549
|
+
"Subscription must be active at the exact finalized authorization snapshot"
|
|
1550
|
+
);
|
|
1427
1551
|
|
|
1428
1552
|
// src/statementGraphWakeGrantAuthorizer.ts
|
|
1553
|
+
import {
|
|
1554
|
+
ChainIdZod as ChainIdZod3,
|
|
1555
|
+
WakeSourceZod as WakeSourceZod2
|
|
1556
|
+
} from "@xyo-network/event-kit-schemas";
|
|
1557
|
+
|
|
1558
|
+
// src/grantEvidence.ts
|
|
1429
1559
|
import {
|
|
1430
1560
|
asWakeGrantObject,
|
|
1431
1561
|
asWakeGrantRevocation,
|
|
1432
|
-
DeploymentIdZod,
|
|
1433
|
-
HashZod as
|
|
1434
|
-
NormalizedAddressZod as
|
|
1562
|
+
DeploymentIdZod as DeploymentIdZod3,
|
|
1563
|
+
HashZod as HashZod6,
|
|
1564
|
+
NormalizedAddressZod as NormalizedAddressZod6,
|
|
1435
1565
|
WakeGrantObjectSchema as WakeGrantObjectSchema3,
|
|
1436
1566
|
WakeGrantRevocationSchema as WakeGrantRevocationSchema3
|
|
1437
1567
|
} from "@xyo-network/event-kit-schemas";
|
|
1438
|
-
function
|
|
1568
|
+
function createDeploymentGrantAuthorizer(options) {
|
|
1439
1569
|
const { viewer } = options;
|
|
1440
1570
|
return Object.freeze({
|
|
1441
1571
|
async authorizePublisher(request) {
|
|
@@ -1446,6 +1576,7 @@ function createStatementGraphWakeGrantAuthorizer(options) {
|
|
|
1446
1576
|
}
|
|
1447
1577
|
const claims = await viewer.claims(request.controller, {
|
|
1448
1578
|
declaredObjectSchemaPrefix: WakeGrantObjectSchema3,
|
|
1579
|
+
...request.exactGrantHash === void 0 ? {} : { objectHash: request.exactGrantHash },
|
|
1449
1580
|
subject: request.publisher
|
|
1450
1581
|
});
|
|
1451
1582
|
const revokedGrantHashes = await readRevocations(viewer, request.controller, request.publisher, atBlock);
|
|
@@ -1497,6 +1628,7 @@ async function readRevocations(viewer, controller, subject, atBlock) {
|
|
|
1497
1628
|
async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHashes) {
|
|
1498
1629
|
if (!isLiveClaim(claim, request.controller, request.publisher, WakeGrantObjectSchema3, atBlock)) return void 0;
|
|
1499
1630
|
if (revokedGrantHashes.has(claim.objectHash)) return void 0;
|
|
1631
|
+
if (request.exactGrantHash !== void 0 && claim.objectHash !== request.exactGrantHash) return void 0;
|
|
1500
1632
|
const object = await viewer.object(claim.objectHash);
|
|
1501
1633
|
const grant = parseExactGrantObject(object, claim.objectHash);
|
|
1502
1634
|
if (grant === void 0) return void 0;
|
|
@@ -1506,9 +1638,14 @@ async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHash
|
|
|
1506
1638
|
if (!grant.permissions.includes("enqueue")) return void 0;
|
|
1507
1639
|
return Object.freeze({
|
|
1508
1640
|
assertedAtBlock: claim.firstBlock,
|
|
1641
|
+
authorizationChainId: request.authorizationChainId,
|
|
1642
|
+
sourceHash: request.sourceHash,
|
|
1643
|
+
filterHash: request.filterHash,
|
|
1644
|
+
subscriptionHash: request.subscriptionHash,
|
|
1509
1645
|
controller: request.controller,
|
|
1510
1646
|
deploymentId: grant.deploymentId,
|
|
1511
1647
|
id: claim.firstEventHash,
|
|
1648
|
+
...request.exactGrantHash === void 0 ? {} : { objectHash: claim.objectHash },
|
|
1512
1649
|
...grant.queueId === void 0 ? {} : { queueId: grant.queueId },
|
|
1513
1650
|
target: grant.publisher
|
|
1514
1651
|
});
|
|
@@ -1519,8 +1656,8 @@ function isLiveClaim(claim, controller, subject, declaredObjectSchema, atBlock)
|
|
|
1519
1656
|
if (claim.declaredObjectSchema !== declaredObjectSchema) return false;
|
|
1520
1657
|
if (!isNonnegativeSafeInteger4(claim.firstBlock) || claim.firstBlock > atBlock) return false;
|
|
1521
1658
|
if (!isNonnegativeSafeInteger4(claim.lastBlock) || claim.lastBlock < claim.firstBlock) return false;
|
|
1522
|
-
if (!
|
|
1523
|
-
return
|
|
1659
|
+
if (!HashZod6.safeParse(claim.firstEventHash).success) return false;
|
|
1660
|
+
return HashZod6.safeParse(claim.objectHash).success;
|
|
1524
1661
|
}
|
|
1525
1662
|
function parseExactGrantObject(object, expectedObjectHash) {
|
|
1526
1663
|
if (object?.assurance !== "resolved") return void 0;
|
|
@@ -1532,13 +1669,16 @@ function parseExactGrantObject(object, expectedObjectHash) {
|
|
|
1532
1669
|
});
|
|
1533
1670
|
}
|
|
1534
1671
|
function assertValidRequest(request) {
|
|
1535
|
-
if (!
|
|
1672
|
+
if (request.exactGrantHash !== void 0 && !HashZod6.safeParse(request.exactGrantHash).success) {
|
|
1673
|
+
throw new TypeError("Exact grant hash must be canonical lowercase hexadecimal");
|
|
1674
|
+
}
|
|
1675
|
+
if (!NormalizedAddressZod6.safeParse(request.controller).success) {
|
|
1536
1676
|
throw new TypeError("Grant controller must be a normalized address");
|
|
1537
1677
|
}
|
|
1538
|
-
if (!
|
|
1678
|
+
if (!NormalizedAddressZod6.safeParse(request.publisher).success) {
|
|
1539
1679
|
throw new TypeError("Grant publisher must be a normalized address");
|
|
1540
1680
|
}
|
|
1541
|
-
if (!
|
|
1681
|
+
if (!DeploymentIdZod3.safeParse(request.deploymentId).success) {
|
|
1542
1682
|
throw new TypeError("Grant deploymentId must contain 1 to 255 characters");
|
|
1543
1683
|
}
|
|
1544
1684
|
if (request.queueId !== void 0 && (request.queueId.length === 0 || request.queueId.length > 255)) {
|
|
@@ -1562,98 +1702,136 @@ function isRecord2(value) {
|
|
|
1562
1702
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1563
1703
|
}
|
|
1564
1704
|
|
|
1705
|
+
// src/statementGraphWakeGrantAuthorizer.ts
|
|
1706
|
+
function createStatementGraphWakeGrantAuthorizer(options) {
|
|
1707
|
+
const authorizationChainId = ChainIdZod3.parse(options.authorizationChainId);
|
|
1708
|
+
const source = WakeSourceZod2.parse(options.source);
|
|
1709
|
+
const grants = createDeploymentGrantAuthorizer({ viewer: options.viewer });
|
|
1710
|
+
return Object.freeze({
|
|
1711
|
+
async authorizePublisher(request) {
|
|
1712
|
+
const sourceHash = await wakeSourceHash(source);
|
|
1713
|
+
if (request.authorizationChainId !== authorizationChainId || request.sourceHash !== sourceHash) {
|
|
1714
|
+
throw new TypeError("Grant request does not match the independently pinned authorization chain and source");
|
|
1715
|
+
}
|
|
1716
|
+
const read = await options.activation.read(request);
|
|
1717
|
+
if (read === void 0) return { enabled: false, atBlock: request.expectedHead };
|
|
1718
|
+
const activation = SourceWakeSubscriptionActivationZod.parse(read);
|
|
1719
|
+
if (activation.atBlock !== request.expectedHead || activation.authorizationChainId !== authorizationChainId || activation.sourceHash !== sourceHash || activation.filterHash !== request.filterHash || activation.subscriptionHash !== request.subscriptionHash || activation.controller !== request.controller || activation.publisher !== request.publisher || activation.deploymentId !== request.deploymentId || activation.queueId !== request.queueId) {
|
|
1720
|
+
throw new TypeError("Finalized subscription activation does not match the exact admission binding");
|
|
1721
|
+
}
|
|
1722
|
+
const decision = await grants.authorizePublisher({ ...request, exactGrantHash: activation.grantHash });
|
|
1723
|
+
if (decision.atBlock !== activation.atBlock) {
|
|
1724
|
+
throw new TypeError("Grant and active subscription snapshots do not identify the same finalized authorization head");
|
|
1725
|
+
}
|
|
1726
|
+
return decision.enabled ? Object.freeze({
|
|
1727
|
+
...decision,
|
|
1728
|
+
grant: Object.freeze({
|
|
1729
|
+
...decision.grant,
|
|
1730
|
+
authorizationChainId,
|
|
1731
|
+
sourceHash,
|
|
1732
|
+
filterHash: activation.filterHash,
|
|
1733
|
+
subscriptionHash: activation.subscriptionHash
|
|
1734
|
+
})
|
|
1735
|
+
}) : decision;
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1565
1740
|
// src/verifyWakeRequest.ts
|
|
1566
1741
|
import {
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1742
|
+
ChainIdZod as ChainIdZod4,
|
|
1743
|
+
DeploymentIdZod as DeploymentIdZod4,
|
|
1744
|
+
HashZod as HashZod7,
|
|
1745
|
+
MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
|
|
1746
|
+
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
|
|
1747
|
+
NormalizedAddressZod as NormalizedAddressZod7,
|
|
1748
|
+
PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2,
|
|
1749
|
+
SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
|
|
1750
|
+
WakeEnvelopeZod as WakeEnvelopeZod2,
|
|
1751
|
+
WakeJwtAudienceZod as WakeJwtAudienceZod2,
|
|
1572
1752
|
WakeJwtHeaderZod,
|
|
1573
1753
|
WakeJwtPayloadZod,
|
|
1574
|
-
WakeJwtSchema
|
|
1754
|
+
WakeJwtSchema,
|
|
1755
|
+
WakeSourceZod as WakeSourceZod3
|
|
1575
1756
|
} from "@xyo-network/event-kit-schemas";
|
|
1576
|
-
import {
|
|
1757
|
+
import {
|
|
1758
|
+
Account,
|
|
1759
|
+
PayloadBuilder as PayloadBuilder5
|
|
1760
|
+
} from "@xyo-network/sdk";
|
|
1761
|
+
import { z as z3 } from "zod";
|
|
1577
1762
|
var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
|
|
1578
1763
|
var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
|
|
1764
|
+
var WakeAdmissionRouteZod = z3.strictObject({
|
|
1765
|
+
audience: WakeJwtAudienceZod2,
|
|
1766
|
+
authorizationChainId: ChainIdZod4,
|
|
1767
|
+
controller: NormalizedAddressZod7,
|
|
1768
|
+
deploymentId: DeploymentIdZod4,
|
|
1769
|
+
queueId: z3.string().min(1).max(255).optional(),
|
|
1770
|
+
source: WakeSourceZod3,
|
|
1771
|
+
filterHash: HashZod7,
|
|
1772
|
+
subscriptionHash: HashZod7,
|
|
1773
|
+
fromPosition: NonnegativeJsonSafeIntegerZod3,
|
|
1774
|
+
maxPositionsPerWake: PositiveJsonSafeIntegerZod2.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
|
|
1775
|
+
maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod3
|
|
1776
|
+
});
|
|
1579
1777
|
async function verifyWakeRequest(input) {
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
const
|
|
1585
|
-
if (!
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (!
|
|
1598
|
-
if (
|
|
1778
|
+
const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
|
|
1779
|
+
if (!routeResult.success || !NonnegativeJsonSafeIntegerZod3.safeParse(input.nowSeconds).success) {
|
|
1780
|
+
return { ok: false, reason: "configuration-invalid" };
|
|
1781
|
+
}
|
|
1782
|
+
const eventResult = WakeEnvelopeZod2.safeParse(input.body);
|
|
1783
|
+
if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
|
|
1784
|
+
const route = routeResult.data;
|
|
1785
|
+
if (route.filterHash !== await sourceWakeFilterHash({
|
|
1786
|
+
schema: SOURCE_WAKE_FILTER_PROFILE2,
|
|
1787
|
+
source: route.source,
|
|
1788
|
+
fromPosition: route.fromPosition,
|
|
1789
|
+
maxPositionsPerWake: route.maxPositionsPerWake
|
|
1790
|
+
})) return { ok: false, reason: "configuration-invalid" };
|
|
1791
|
+
const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
|
|
1792
|
+
if (event.authorizationChainId !== route.authorizationChainId) {
|
|
1793
|
+
return { ok: false, reason: "event-authorization-chain-mismatch" };
|
|
1794
|
+
}
|
|
1795
|
+
if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
|
|
1796
|
+
if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
|
|
1599
1797
|
if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
|
|
1600
|
-
return
|
|
1798
|
+
return { ok: false, reason: "event-target-mismatch" };
|
|
1601
1799
|
}
|
|
1602
|
-
if (event.
|
|
1603
|
-
if (event.
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
const jwtResult = await verifyJwtProfile(input.jwt, route.audience, nowSeconds);
|
|
1607
|
-
if (!jwtResult.ok) return jwtResult;
|
|
1800
|
+
if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
|
|
1801
|
+
if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
|
|
1802
|
+
const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
|
|
1803
|
+
if (!jwt.ok) return jwt;
|
|
1608
1804
|
let eventHash;
|
|
1609
1805
|
try {
|
|
1610
|
-
eventHash = await
|
|
1611
|
-
} catch
|
|
1612
|
-
return
|
|
1613
|
-
}
|
|
1614
|
-
if (eventHash !== jwtResult.jwt.eventHash) return reject2("event-hash-mismatch");
|
|
1615
|
-
const frozenEvent = Object.freeze({ ...event });
|
|
1616
|
-
const verified = Object.freeze({
|
|
1617
|
-
audience: route.audience,
|
|
1618
|
-
deploymentId: event.deploymentId,
|
|
1619
|
-
event: frozenEvent,
|
|
1620
|
-
eventHash,
|
|
1621
|
-
expiresAt: jwtResult.jwt.expiresAt,
|
|
1622
|
-
fromBlock: event.fromBlock,
|
|
1623
|
-
issuedAt: event.issuedAt,
|
|
1624
|
-
jwtId: jwtResult.jwt.jwtId,
|
|
1625
|
-
publisher: jwtResult.jwt.publisher,
|
|
1626
|
-
...event.queueId === void 0 ? {} : { queueId: event.queueId },
|
|
1627
|
-
route,
|
|
1628
|
-
toBlock: event.toBlock,
|
|
1629
|
-
tokenIssuedAt: jwtResult.jwt.issuedAt,
|
|
1630
|
-
wakeId: event.wakeId
|
|
1631
|
-
});
|
|
1632
|
-
return Object.freeze({ ok: true, verified });
|
|
1633
|
-
}
|
|
1634
|
-
function validateConfiguration(route, nowSeconds) {
|
|
1635
|
-
if (!WakeJwtAudienceZod.safeParse(route.audience).success) {
|
|
1636
|
-
return reject2("configuration-invalid", ["audience must not be empty"]);
|
|
1637
|
-
}
|
|
1638
|
-
if (!DeploymentIdZod2.safeParse(route.deploymentId).success) {
|
|
1639
|
-
return reject2("configuration-invalid", ["deploymentId must contain 1 to 255 characters"]);
|
|
1640
|
-
}
|
|
1641
|
-
if (route.queueId !== void 0 && (route.queueId.length === 0 || route.queueId.length > 255)) {
|
|
1642
|
-
return reject2("configuration-invalid", ["queueId must contain 1 to 255 characters when configured"]);
|
|
1643
|
-
}
|
|
1644
|
-
if (!NormalizedAddressZod5.safeParse(route.controller).success) {
|
|
1645
|
-
return reject2("configuration-invalid", ["controller must be a normalized 40-character lowercase-hex address"]);
|
|
1646
|
-
}
|
|
1647
|
-
if (!isNonnegativeSafeInteger5(nowSeconds)) {
|
|
1648
|
-
return reject2("configuration-invalid", ["nowSeconds must be a nonnegative safe integer"]);
|
|
1649
|
-
}
|
|
1650
|
-
if (!Number.isSafeInteger(route.maxBlocksPerWake) || route.maxBlocksPerWake < 1) {
|
|
1651
|
-
return reject2("configuration-invalid", ["maxBlocksPerWake must be a positive safe integer"]);
|
|
1652
|
-
}
|
|
1653
|
-
if (!isNonnegativeSafeInteger5(route.maxGrantLagBlocks)) {
|
|
1654
|
-
return reject2("configuration-invalid", ["maxGrantLagBlocks must be a nonnegative safe integer"]);
|
|
1806
|
+
eventHash = await PayloadBuilder5.dataHash(event);
|
|
1807
|
+
} catch {
|
|
1808
|
+
return { ok: false, reason: "event-hash-failed" };
|
|
1655
1809
|
}
|
|
1656
|
-
return
|
|
1810
|
+
if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
|
|
1811
|
+
const sourceHash = await wakeSourceHash(route.source);
|
|
1812
|
+
return Object.freeze({
|
|
1813
|
+
ok: true,
|
|
1814
|
+
verified: Object.freeze({
|
|
1815
|
+
audience: route.audience,
|
|
1816
|
+
deploymentId: event.deploymentId,
|
|
1817
|
+
event,
|
|
1818
|
+
eventHash,
|
|
1819
|
+
expiresAt: jwt.jwt.expiresAt,
|
|
1820
|
+
fromPosition: event.fromPosition,
|
|
1821
|
+
issuedAt: event.issuedAt,
|
|
1822
|
+
jwtId: jwt.jwt.jwtId,
|
|
1823
|
+
publisher: jwt.jwt.publisher,
|
|
1824
|
+
...event.queueId === void 0 ? {} : { queueId: event.queueId },
|
|
1825
|
+
route: Object.freeze({
|
|
1826
|
+
...route,
|
|
1827
|
+
source: Object.freeze(route.source),
|
|
1828
|
+
sourceHash
|
|
1829
|
+
}),
|
|
1830
|
+
toPosition: event.toPosition,
|
|
1831
|
+
tokenIssuedAt: jwt.jwt.issuedAt,
|
|
1832
|
+
wakeId: event.wakeId
|
|
1833
|
+
})
|
|
1834
|
+
});
|
|
1657
1835
|
}
|
|
1658
1836
|
async function verifyJwtProfile(token, audience, nowSeconds) {
|
|
1659
1837
|
let verified;
|
|
@@ -1685,11 +1863,11 @@ async function verifyJwtProfile(token, audience, nowSeconds) {
|
|
|
1685
1863
|
function parseStrictJwtWire(header, payload, audience) {
|
|
1686
1864
|
const headerResult = WakeJwtHeaderZod.safeParse(header);
|
|
1687
1865
|
if (!headerResult.success) {
|
|
1688
|
-
if (!
|
|
1866
|
+
if (!NormalizedAddressZod7.safeParse(header.kid).success) return reject2("jwt-identity-invalid");
|
|
1689
1867
|
return reject2("jwt-header-invalid", zodDetails(headerResult.error.issues));
|
|
1690
1868
|
}
|
|
1691
1869
|
if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
|
|
1692
|
-
if (!
|
|
1870
|
+
if (!NormalizedAddressZod7.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
|
|
1693
1871
|
if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
|
|
1694
1872
|
if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
|
|
1695
1873
|
if (!isNonnegativeSafeInteger5(payload.iat) || !isNonnegativeSafeInteger5(payload.exp) || payload.exp <= payload.iat) {
|
|
@@ -1714,13 +1892,16 @@ function classifySdkJwtFailure(reasons) {
|
|
|
1714
1892
|
}
|
|
1715
1893
|
return "jwt-invalid";
|
|
1716
1894
|
}
|
|
1717
|
-
function
|
|
1718
|
-
|
|
1895
|
+
function malformedWake(body, issues) {
|
|
1896
|
+
const details = zodDetails(issues);
|
|
1897
|
+
if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
|
|
1898
|
+
if (isRecord3(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId")) return reject2("wake-id-invalid", details);
|
|
1899
|
+
return reject2("event-malformed", details);
|
|
1719
1900
|
}
|
|
1720
1901
|
function hasInvertedSafeRange(value) {
|
|
1721
1902
|
if (!isRecord3(value)) return false;
|
|
1722
|
-
const {
|
|
1723
|
-
return typeof
|
|
1903
|
+
const { fromPosition, toPosition } = value;
|
|
1904
|
+
return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger5(fromPosition) && isNonnegativeSafeInteger5(toPosition) && fromPosition > toPosition;
|
|
1724
1905
|
}
|
|
1725
1906
|
function isNonnegativeSafeInteger5(value) {
|
|
1726
1907
|
return Number.isSafeInteger(value) && value >= 0;
|
|
@@ -1728,16 +1909,6 @@ function isNonnegativeSafeInteger5(value) {
|
|
|
1728
1909
|
function isRecord3(value) {
|
|
1729
1910
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1730
1911
|
}
|
|
1731
|
-
function snapshotRoute(route) {
|
|
1732
|
-
return Object.freeze({
|
|
1733
|
-
audience: route.audience,
|
|
1734
|
-
controller: route.controller,
|
|
1735
|
-
deploymentId: route.deploymentId,
|
|
1736
|
-
maxBlocksPerWake: route.maxBlocksPerWake,
|
|
1737
|
-
maxGrantLagBlocks: route.maxGrantLagBlocks,
|
|
1738
|
-
...route.queueId === void 0 ? {} : { queueId: route.queueId }
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1741
1912
|
function zodDetails(issues) {
|
|
1742
1913
|
return issues.map((issue) => {
|
|
1743
1914
|
const path = issue.path.map(String).join(".") || "<root>";
|
|
@@ -1773,9 +1944,12 @@ async function drainQueue(queue, handler) {
|
|
|
1773
1944
|
}
|
|
1774
1945
|
}
|
|
1775
1946
|
export {
|
|
1947
|
+
AdmittedWakeZod,
|
|
1776
1948
|
EVENT_KIT_MAX_FUTURE_IAT_SECONDS,
|
|
1777
1949
|
EVENT_KIT_MAX_JWT_LIFETIME_SECONDS,
|
|
1778
1950
|
EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS,
|
|
1951
|
+
SourceWakeSubscriptionActivationZod,
|
|
1952
|
+
WakeAdmissionRouteZod,
|
|
1779
1953
|
WakeSubscriptionDecryptionError,
|
|
1780
1954
|
admitAndEnqueue,
|
|
1781
1955
|
beginPublisherEndpointChallengeAttempt,
|
|
@@ -1808,12 +1982,16 @@ export {
|
|
|
1808
1982
|
generateWakeSubscriptionEncryptionKeyPair,
|
|
1809
1983
|
handshakeObjectHash,
|
|
1810
1984
|
issuePublisherEndpointChallenge,
|
|
1985
|
+
parseAdmittedWake,
|
|
1811
1986
|
resolvePreferredEncryptionKey,
|
|
1812
1987
|
selectPublisherService,
|
|
1813
1988
|
selectSubscriptionActivation,
|
|
1989
|
+
sourceWakeFilterHash,
|
|
1814
1990
|
validateAndDecryptPublisherSubscription,
|
|
1815
1991
|
verifyEndpointChallengeResponse,
|
|
1816
1992
|
verifyWakeRequest,
|
|
1993
|
+
wakeSourceHash,
|
|
1994
|
+
wakeSourcesEqual,
|
|
1817
1995
|
wakeSubscriptionAadBytes,
|
|
1818
1996
|
wakeSubscriptionPlaintextBytes,
|
|
1819
1997
|
wakeSubscriptionPlaintextFromBytes,
|