@blockcast/mmt-container 1.0.1 → 2.0.0-main.609e08d7e29f
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 +9 -7
- package/dist/browser-runtime.js +6972 -6693
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +7 -7
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +65 -230
- package/dist/carp-catalog.js.map +3 -3
- package/dist/catalog-fec-params.js.map +1 -1
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/delivery-catalog.js +3 -0
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +25 -1
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +208 -15
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.js.map +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/held-layer-admission.d.ts +8 -0
- package/dist/held-layer-admission.d.ts.map +1 -1
- package/dist/held-layer-admission.js +22 -1
- package/dist/held-layer-admission.js.map +2 -2
- package/dist/iso-block-locator.js.map +1 -1
- package/dist/mfu-reassembler.js.map +1 -1
- package/dist/mmtp-container-parser.js.map +1 -1
- package/dist/mmtp-container-parser.no-modules.js +146 -332
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js.map +1 -1
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +61 -15
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +11 -6
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +47 -10
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.d.ts +8 -8
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +171 -75
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-input.d.ts +5 -5
- package/dist/multicast-input.d.ts.map +1 -1
- package/dist/multicast-input.js +126 -49
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +11 -4
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +7914 -7659
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +16 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +120 -5
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +2 -2
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +100 -288
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.js.map +1 -1
- package/dist/signaling.js.map +1 -1
- package/dist/timestamp-codec.js.map +1 -1
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js.map +1 -1
- package/package.json +5 -5
- package/schemas/catalog.schema.json +15 -7
- package/src/carp-catalog.ts +14 -13
- package/src/fec-manager.ts +89 -14
- package/src/held-layer-admission.ts +38 -1
- package/src/mmtp-fec-session.ts +20 -12
- package/src/mmtp-router.ts +65 -16
- package/src/multicast-auth-catalog.ts +43 -36
- package/src/multicast-input.ts +28 -8
- package/src/multicast-manager.ts +38 -34
- package/src/multicast-transport.ts +74 -7
- package/test-vectors/bc-provenance-v2.json +614 -175
- package/test-vectors/catalog/README.md +24 -8
- package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
- package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
- package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
- package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
- package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
- package/test-vectors/catalog/section-codes.json +8 -3
- package/test-vectors/catalog-multicast-auth-v1.json +0 -1
- package/test-vectors/catalog-validation.json +3 -3
- package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
|
@@ -382,14 +382,11 @@ var BlockcastMmt = (() => {
|
|
|
382
382
|
dataOffset: off + REPAIR_FEC_PID_SIZE
|
|
383
383
|
};
|
|
384
384
|
}
|
|
385
|
-
function
|
|
386
|
-
return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header));
|
|
387
|
-
}
|
|
388
|
-
function moqRepairFrameFrom(frame, repairId) {
|
|
385
|
+
function moqRepairFrameFrom(frame, repairId, checkRsId = true) {
|
|
389
386
|
if (!repairId)
|
|
390
387
|
return null;
|
|
391
388
|
const symbolData = frame.subarray(repairId.dataOffset);
|
|
392
|
-
if (repairId.
|
|
389
|
+
if (repairId.ssbLength === 0 || checkRsId && repairId.rsId >= repairId.rsbLength || symbolData.length === 0)
|
|
393
390
|
return null;
|
|
394
391
|
return {
|
|
395
392
|
packetId: repairId.packetId,
|
|
@@ -400,6 +397,23 @@ var BlockcastMmt = (() => {
|
|
|
400
397
|
symbolData
|
|
401
398
|
};
|
|
402
399
|
}
|
|
400
|
+
function admitMmtpRepairObject(packet, header, symbolSize, algorithm) {
|
|
401
|
+
if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2)
|
|
402
|
+
return null;
|
|
403
|
+
if (header.headerLength !== MMTP_HEADER_SIZE || packet.length !== MMTP_HEADER_SIZE + REPAIR_FEC_PID_SIZE + symbolSize) {
|
|
404
|
+
return { reject: "repairLength" };
|
|
405
|
+
}
|
|
406
|
+
const frame = moqRepairFrameFrom(packet, readRepairFecPayloadIdFields(packet, header), false);
|
|
407
|
+
if (!frame)
|
|
408
|
+
return null;
|
|
409
|
+
if (frame.rsId >= frame.rsbLength)
|
|
410
|
+
return { frame, reject: "repairRsId" };
|
|
411
|
+
const blockSymbols = frame.ssbLength + frame.rsbLength;
|
|
412
|
+
if (algorithm === "reed-solomon" ? blockSymbols > 255 : algorithm === "raptorq" && blockSymbols - 1 > 16777215) {
|
|
413
|
+
return { frame, reject: "repairSchemeBound" };
|
|
414
|
+
}
|
|
415
|
+
return { frame };
|
|
416
|
+
}
|
|
403
417
|
|
|
404
418
|
// ../mmt-base/dist/net-constants.js
|
|
405
419
|
var DEFAULT_AMT_RELAY_PORT = 2268;
|
|
@@ -477,254 +491,6 @@ var BlockcastMmt = (() => {
|
|
|
477
491
|
return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0;
|
|
478
492
|
}
|
|
479
493
|
|
|
480
|
-
// ../mmt-base/dist/multicast-catalog.js
|
|
481
|
-
var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
482
|
-
var UINT32_MAX = 4294967295;
|
|
483
|
-
var AUTH_MEMBERS = /* @__PURE__ */ new Set(["scheme", "version", "sig", "keys", "minEpoch"]);
|
|
484
|
-
var KEY_MEMBERS = /* @__PURE__ */ new Set(["kid", "publicKey", "firstEpoch"]);
|
|
485
|
-
var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
|
|
486
|
-
"manifestPort",
|
|
487
|
-
"streamId",
|
|
488
|
-
"senderAddress",
|
|
489
|
-
"maxAuthDelayMs",
|
|
490
|
-
"manifestTimerMs",
|
|
491
|
-
"manifestTrack"
|
|
492
|
-
]);
|
|
493
|
-
function isObject(value) {
|
|
494
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
495
|
-
}
|
|
496
|
-
function isNonEmptyString(value) {
|
|
497
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
498
|
-
}
|
|
499
|
-
function isIntegerIn(value, min, max) {
|
|
500
|
-
return Number.isSafeInteger(value) && value >= min && value <= max;
|
|
501
|
-
}
|
|
502
|
-
function multicastEndpointSource(endpoint) {
|
|
503
|
-
if (!isObject(endpoint))
|
|
504
|
-
return void 0;
|
|
505
|
-
const sourceAddress = endpoint["sourceAddress"];
|
|
506
|
-
if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress))
|
|
507
|
-
return void 0;
|
|
508
|
-
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
509
|
-
const protocol = endpoint["protocol"];
|
|
510
|
-
if (protocol !== void 0 && protocol !== mode)
|
|
511
|
-
return void 0;
|
|
512
|
-
const auth = endpoint["auth"];
|
|
513
|
-
const senderAddress = isObject(auth) ? auth["senderAddress"] : void 0;
|
|
514
|
-
if (mode === "ssm")
|
|
515
|
-
return senderAddress === void 0 ? { mode, source: sourceAddress } : void 0;
|
|
516
|
-
if (auth === void 0)
|
|
517
|
-
return { mode };
|
|
518
|
-
return isNonEmptyString(senderAddress) ? { mode, source: senderAddress } : void 0;
|
|
519
|
-
}
|
|
520
|
-
function authIssues(auth) {
|
|
521
|
-
const base = "$.multicast.auth";
|
|
522
|
-
const issue = (path, message) => [
|
|
523
|
-
{ code: "multicast_auth", path: `${base}${path}`, message }
|
|
524
|
-
];
|
|
525
|
-
if (!isObject(auth))
|
|
526
|
-
return issue("", "auth must be an object");
|
|
527
|
-
const unknown = Object.keys(auth).filter((key) => !AUTH_MEMBERS.has(key));
|
|
528
|
-
if (unknown.length > 0) {
|
|
529
|
-
return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -01 7.2.6 defines scheme, version, sig, keys and minEpoch`);
|
|
530
|
-
}
|
|
531
|
-
if (auth["scheme"] !== "bc-provenance")
|
|
532
|
-
return issue(".scheme", 'scheme must be "bc-provenance"');
|
|
533
|
-
if (auth["version"] !== 2)
|
|
534
|
-
return issue(".version", "version must be 2 (bc-provenance version 2, draft -01 7.2)");
|
|
535
|
-
if (auth["sig"] !== "ed25519")
|
|
536
|
-
return issue(".sig", 'sig must be "ed25519"');
|
|
537
|
-
const keys = auth["keys"];
|
|
538
|
-
if (!Array.isArray(keys) || keys.length < 1 || keys.length > 4) {
|
|
539
|
-
return issue(".keys", "keys must be an array of one to four {kid, publicKey, firstEpoch} objects");
|
|
540
|
-
}
|
|
541
|
-
const kids = /* @__PURE__ */ new Set();
|
|
542
|
-
const firstEpochs = /* @__PURE__ */ new Set();
|
|
543
|
-
for (let index = 0; index < keys.length; index++) {
|
|
544
|
-
const key = keys[index];
|
|
545
|
-
const at = `.keys[${index}]`;
|
|
546
|
-
if (!isObject(key) || Object.keys(key).some((member) => !KEY_MEMBERS.has(member))) {
|
|
547
|
-
return issue(at, "a key is exactly {kid, publicKey, firstEpoch}");
|
|
548
|
-
}
|
|
549
|
-
if (!isIntegerIn(key["kid"], 0, UINT32_MAX))
|
|
550
|
-
return issue(`${at}.kid`, "kid must be an unsigned 32-bit integer");
|
|
551
|
-
if (typeof key["publicKey"] !== "string" || !ED25519_PUBLIC_KEY_BASE64URL.test(key["publicKey"])) {
|
|
552
|
-
return issue(`${at}.publicKey`, "publicKey must be a 32-octet Ed25519 key in canonical unpadded base64url");
|
|
553
|
-
}
|
|
554
|
-
if (!isIntegerIn(key["firstEpoch"], 0, UINT32_MAX)) {
|
|
555
|
-
return issue(`${at}.firstEpoch`, "firstEpoch must be an unsigned 32-bit integer");
|
|
556
|
-
}
|
|
557
|
-
if (kids.has(key["kid"]))
|
|
558
|
-
return issue(`${at}.kid`, `kid ${key["kid"]} is repeated; the catalog holds no key set`);
|
|
559
|
-
if (firstEpochs.has(key["firstEpoch"])) {
|
|
560
|
-
return issue(`${at}.firstEpoch`, `firstEpoch ${key["firstEpoch"]} is repeated; the catalog holds no key set`);
|
|
561
|
-
}
|
|
562
|
-
kids.add(key["kid"]);
|
|
563
|
-
firstEpochs.add(key["firstEpoch"]);
|
|
564
|
-
}
|
|
565
|
-
if (!isIntegerIn(auth["minEpoch"], 0, UINT32_MAX)) {
|
|
566
|
-
return issue(".minEpoch", "minEpoch must be an unsigned 32-bit integer");
|
|
567
|
-
}
|
|
568
|
-
return [];
|
|
569
|
-
}
|
|
570
|
-
function endpointAuthIssue(endpoint, mode, at) {
|
|
571
|
-
const auth = endpoint["auth"];
|
|
572
|
-
if (!isObject(auth))
|
|
573
|
-
return { path: `${at}.auth`, message: "auth must be an object" };
|
|
574
|
-
const unknown = Object.keys(auth).filter((key) => !ENDPOINT_AUTH_MEMBERS.has(key));
|
|
575
|
-
if (unknown.length > 0)
|
|
576
|
-
return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(", ")}` };
|
|
577
|
-
if (!isIntegerIn(auth["manifestPort"], 1, 65535)) {
|
|
578
|
-
return { path: `${at}.auth.manifestPort`, message: "manifestPort must be an integer from 1 to 65535" };
|
|
579
|
-
}
|
|
580
|
-
if (!isIntegerIn(auth["streamId"], 0, UINT32_MAX)) {
|
|
581
|
-
return { path: `${at}.auth.streamId`, message: "streamId must be an unsigned 32-bit integer" };
|
|
582
|
-
}
|
|
583
|
-
if (mode === "ssm" && auth["senderAddress"] !== void 0) {
|
|
584
|
-
return { path: `${at}.auth.senderAddress`, message: "senderAddress must not appear on an SSM endpoint; S is sourceAddress" };
|
|
585
|
-
}
|
|
586
|
-
if (mode === "asm" && !isNonEmptyString(auth["senderAddress"])) {
|
|
587
|
-
return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress" };
|
|
588
|
-
}
|
|
589
|
-
if (auth["maxAuthDelayMs"] !== void 0 && !isIntegerIn(auth["maxAuthDelayMs"], 50, 1e3)) {
|
|
590
|
-
return { path: `${at}.auth.maxAuthDelayMs`, message: "maxAuthDelayMs must be an integer from 50 to 1000" };
|
|
591
|
-
}
|
|
592
|
-
if (auth["manifestTimerMs"] !== void 0 && !isIntegerIn(auth["manifestTimerMs"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
593
|
-
return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be an integer of at least 1" };
|
|
594
|
-
}
|
|
595
|
-
if (auth["manifestTrack"] !== void 0 && !isNonEmptyString(auth["manifestTrack"])) {
|
|
596
|
-
return { path: `${at}.auth.manifestTrack`, message: "manifestTrack must be a non-empty string" };
|
|
597
|
-
}
|
|
598
|
-
if (!isIntegerIn(endpoint["bandwidth"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
599
|
-
return { path: `${at}.bandwidth`, message: "a protected endpoint requires bandwidth, which bounds the receiver's hold queues" };
|
|
600
|
-
}
|
|
601
|
-
return void 0;
|
|
602
|
-
}
|
|
603
|
-
var CATALOG_NAMESPACE = /* @__PURE__ */ Symbol.for("@blockcast/mmt-base/catalogNamespace");
|
|
604
|
-
function multicastCatalogIssues(catalog, options = {}) {
|
|
605
|
-
if (!isObject(catalog))
|
|
606
|
-
return [];
|
|
607
|
-
const issues = [];
|
|
608
|
-
if ("multicast.auth" in catalog) {
|
|
609
|
-
issues.push({
|
|
610
|
-
code: "multicast_auth",
|
|
611
|
-
path: '$["multicast.auth"]',
|
|
612
|
-
message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -01 7.2.6)'
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
const multicast = catalog["multicast"];
|
|
616
|
-
if (!isObject(multicast))
|
|
617
|
-
return issues;
|
|
618
|
-
const protectedCatalog = multicast["auth"] !== void 0;
|
|
619
|
-
if (protectedCatalog)
|
|
620
|
-
issues.push(...authIssues(multicast["auth"]));
|
|
621
|
-
const endpoints = multicast["endpoints"];
|
|
622
|
-
if (!Array.isArray(endpoints))
|
|
623
|
-
return issues;
|
|
624
|
-
const tracks = Array.isArray(catalog["tracks"]) ? catalog["tracks"] : [];
|
|
625
|
-
const namespaceOf = /* @__PURE__ */ new Map();
|
|
626
|
-
for (const track of tracks) {
|
|
627
|
-
if (isObject(track) && typeof track["name"] === "string")
|
|
628
|
-
namespaceOf.set(track["name"], track["namespace"]);
|
|
629
|
-
}
|
|
630
|
-
const tagged = catalog[CATALOG_NAMESPACE];
|
|
631
|
-
let referenceNamespace = options.catalogNamespace ?? (typeof tagged === "string" ? tagged : void 0);
|
|
632
|
-
const sources = [];
|
|
633
|
-
for (let index = 0; index < endpoints.length; index++) {
|
|
634
|
-
const endpoint = endpoints[index];
|
|
635
|
-
const at = `$.multicast.endpoints[${index}]`;
|
|
636
|
-
const fail = (code, path, message) => {
|
|
637
|
-
issues.push({ code, path, message, endpointIndex: index });
|
|
638
|
-
};
|
|
639
|
-
sources.push(void 0);
|
|
640
|
-
if (!isObject(endpoint)) {
|
|
641
|
-
fail("multicast_endpoint", at, "endpoint must be an object");
|
|
642
|
-
continue;
|
|
643
|
-
}
|
|
644
|
-
const protocol = endpoint["protocol"];
|
|
645
|
-
if (protocol !== void 0 && protocol !== "ssm" && protocol !== "asm") {
|
|
646
|
-
fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -01 4.2)`);
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
const hasAuth = endpoint["auth"] !== void 0;
|
|
650
|
-
if (protectedCatalog !== hasAuth) {
|
|
651
|
-
fail("multicast_endpoint_auth", `${at}.auth`, protectedCatalog ? "every endpoint requires auth when multicast carries auth; a receiver ignores this endpoint" : "auth names no key without multicast.auth; a receiver ignores this endpoint");
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
const sourceAddress = endpoint["sourceAddress"];
|
|
655
|
-
if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress)) {
|
|
656
|
-
fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be a non-empty string");
|
|
657
|
-
continue;
|
|
658
|
-
}
|
|
659
|
-
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
660
|
-
if (protocol !== void 0 && protocol !== mode) {
|
|
661
|
-
fail("multicast_endpoint", `${at}.protocol`, `protocol ${JSON.stringify(protocol)} disagrees with sourceAddress, which selects ${mode}`);
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
if (!isIntegerIn(endpoint["port"], 1, 65535)) {
|
|
665
|
-
fail("multicast_endpoint", `${at}.port`, "port must be an integer from 1 to 65535");
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
if (hasAuth) {
|
|
669
|
-
const authIssue = endpointAuthIssue(endpoint, mode, at);
|
|
670
|
-
if (authIssue) {
|
|
671
|
-
fail("multicast_endpoint_auth", authIssue.path, authIssue.message);
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
sources[index] = multicastEndpointSource(endpoint);
|
|
676
|
-
const refs = Array.isArray(endpoint["tracks"]) ? endpoint["tracks"] : [];
|
|
677
|
-
for (let refIndex = 0; refIndex < refs.length; refIndex++) {
|
|
678
|
-
const ref = refs[refIndex];
|
|
679
|
-
const name = isObject(ref) ? ref["name"] : ref;
|
|
680
|
-
if (typeof name !== "string")
|
|
681
|
-
continue;
|
|
682
|
-
const namespace = namespaceOf.get(name);
|
|
683
|
-
if (typeof namespace !== "string")
|
|
684
|
-
continue;
|
|
685
|
-
referenceNamespace ?? (referenceNamespace = namespace);
|
|
686
|
-
if (namespace !== referenceNamespace) {
|
|
687
|
-
fail("multicast_track", `${at}.tracks[${refIndex}].name`, `track ${JSON.stringify(name)} declares namespace ${JSON.stringify(namespace)}, not the catalog track's ${JSON.stringify(referenceNamespace)}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const tupleOf = (index) => {
|
|
692
|
-
const endpoint = endpoints[index];
|
|
693
|
-
const auth = endpoint["auth"];
|
|
694
|
-
return {
|
|
695
|
-
sg: JSON.stringify([sources[index].source ?? null, endpoint["groupAddress"]]),
|
|
696
|
-
port: endpoint["port"],
|
|
697
|
-
manifestPort: isObject(auth) ? auth["manifestPort"] : void 0
|
|
698
|
-
};
|
|
699
|
-
};
|
|
700
|
-
for (let index = 0; index < endpoints.length; index++) {
|
|
701
|
-
if (!sources[index])
|
|
702
|
-
continue;
|
|
703
|
-
const own = tupleOf(index);
|
|
704
|
-
const at = `$.multicast.endpoints[${index}]`;
|
|
705
|
-
if (own.manifestPort !== void 0 && own.manifestPort === own.port) {
|
|
706
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: "manifestPort must differ from the endpoint's own port", endpointIndex: index });
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
for (let other = 0; other < endpoints.length; other++) {
|
|
710
|
-
if (other === index || !sources[other])
|
|
711
|
-
continue;
|
|
712
|
-
const peer = tupleOf(other);
|
|
713
|
-
if (peer.sg !== own.sg)
|
|
714
|
-
continue;
|
|
715
|
-
if (peer.port === own.port) {
|
|
716
|
-
issues.push({ code: "multicast_endpoint", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
if (own.manifestPort !== void 0 && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {
|
|
720
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });
|
|
721
|
-
break;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return issues;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
494
|
// ../mmt-base/dist/fec-catalog.js
|
|
729
495
|
var DEFAULT_SCHEMES = ["reed-solomon", "raptorq"];
|
|
730
496
|
var DEFAULT_PROFILES = ["moq-object-v1", "iso-ssbg1-v1"];
|
|
@@ -8061,7 +7827,7 @@ var BlockcastMmt = (() => {
|
|
|
8061
7827
|
}
|
|
8062
7828
|
});
|
|
8063
7829
|
}
|
|
8064
|
-
function validateCmsfMmtpCatalog(value,
|
|
7830
|
+
function validateCmsfMmtpCatalog(value, _options = {}) {
|
|
8065
7831
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8066
7832
|
catalogError("catalog_object", "$", "catalog must be an object");
|
|
8067
7833
|
}
|
|
@@ -8252,8 +8018,6 @@ var BlockcastMmt = (() => {
|
|
|
8252
8018
|
}
|
|
8253
8019
|
}
|
|
8254
8020
|
}
|
|
8255
|
-
const [multicastIssue] = multicastCatalogIssues(catalog, options);
|
|
8256
|
-
if (multicastIssue) catalogError(multicastIssue.code, multicastIssue.path, multicastIssue.message);
|
|
8257
8021
|
validateArchive(catalog);
|
|
8258
8022
|
return catalog;
|
|
8259
8023
|
}
|
|
@@ -8356,9 +8120,10 @@ var BlockcastMmt = (() => {
|
|
|
8356
8120
|
function heldLayerAnchorHorizon(retentionBlocks, k) {
|
|
8357
8121
|
return retentionBlocks === void 0 || k <= 0 ? HORIZON_CAP : Math.min(HORIZON_CAP, retentionBlocks * k);
|
|
8358
8122
|
}
|
|
8359
|
-
var _layerNames, _states;
|
|
8123
|
+
var _layerNames, _states, _HeldLayerAdmission_instances, admit_fn;
|
|
8360
8124
|
var HeldLayerAdmission = class {
|
|
8361
8125
|
constructor(layers, heldLayers = [0]) {
|
|
8126
|
+
__privateAdd(this, _HeldLayerAdmission_instances);
|
|
8362
8127
|
__privateAdd(this, _layerNames);
|
|
8363
8128
|
__privateAdd(this, _states);
|
|
8364
8129
|
const held = new Set(heldLayers);
|
|
@@ -8376,56 +8141,78 @@ var BlockcastMmt = (() => {
|
|
|
8376
8141
|
* @returns undefined to admit, `'discard'`, or the fail-closed reason.
|
|
8377
8142
|
*/
|
|
8378
8143
|
admit(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier) {
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8144
|
+
return __privateMethod(this, _HeldLayerAdmission_instances, admit_fn).call(this, repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, true);
|
|
8145
|
+
}
|
|
8146
|
+
/**
|
|
8147
|
+
* The core-00 7.5 carve-out for a repair object about to be discarded
|
|
8148
|
+
* (RS_ID or scheme bound): whether its own RSB_length, below a held layer's
|
|
8149
|
+
* total on that layer's track or on an anchored block, fails the instance
|
|
8150
|
+
* closed under 7.3.2. Leaves the anchored state as it was: a discarded
|
|
8151
|
+
* object anchors nothing.
|
|
8152
|
+
*/
|
|
8153
|
+
failsClosed(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier) {
|
|
8154
|
+
const saved = __privateGet(this, _states).map((state) => ({ ...state }));
|
|
8155
|
+
try {
|
|
8156
|
+
const verdict = __privateMethod(this, _HeldLayerAdmission_instances, admit_fn).call(this, repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, false);
|
|
8157
|
+
return typeof verdict === "object" ? verdict : void 0;
|
|
8158
|
+
} finally {
|
|
8159
|
+
saved.forEach((state, i) => {
|
|
8160
|
+
__privateGet(this, _states)[i] = state;
|
|
8161
|
+
});
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
};
|
|
8165
|
+
_layerNames = new WeakMap();
|
|
8166
|
+
_states = new WeakMap();
|
|
8167
|
+
_HeldLayerAdmission_instances = new WeakSet();
|
|
8168
|
+
admit_fn = function(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, retroactive) {
|
|
8169
|
+
if (repairTrackName !== void 0 && !__privateGet(this, _layerNames).has(repairTrackName)) return "discard";
|
|
8170
|
+
const farAhead = sourceFrontier !== void 0 && serial32Precedes(sourceFrontier, ssStart) && serial32Distance(sourceFrontier, ssStart) > horizon;
|
|
8171
|
+
for (const state of __privateGet(this, _states)) {
|
|
8172
|
+
let code;
|
|
8173
|
+
if (farAhead) {
|
|
8174
|
+
if (rsbLength < state.total) {
|
|
8175
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
8176
|
+
else if (state.anchor !== void 0 || state.settled) code = "below_anchored_total";
|
|
8177
|
+
}
|
|
8178
|
+
} else {
|
|
8179
|
+
if (state.newest === void 0 || serial32Precedes(state.newest, ssStart)) state.newest = ssStart;
|
|
8180
|
+
const stale = (s) => serial32Distance(s, state.newest) > horizon;
|
|
8181
|
+
if (state.latestBelow !== void 0 && stale(state.latestBelow)) {
|
|
8182
|
+
delete state.latestBelow;
|
|
8183
|
+
delete state.latestBelowRsb;
|
|
8184
|
+
}
|
|
8185
|
+
if (state.anchor !== void 0 && stale(state.anchor)) state.settled = true;
|
|
8186
|
+
if (rsbLength < state.total) {
|
|
8187
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
8188
|
+
else if (stale(ssStart)) continue;
|
|
8189
|
+
else if (state.settled || state.anchor !== void 0 && !serial32Precedes(ssStart, state.anchor)) code = "below_anchored_total";
|
|
8190
|
+
else if (state.latestBelow === void 0 || serial32Precedes(state.latestBelow, ssStart)) {
|
|
8191
|
+
state.latestBelow = ssStart;
|
|
8192
|
+
state.latestBelowRsb = rsbLength;
|
|
8394
8193
|
}
|
|
8395
|
-
|
|
8396
|
-
if (
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
state.latestBelow = ssStart;
|
|
8402
|
-
state.latestBelowRsb = rsbLength;
|
|
8403
|
-
}
|
|
8404
|
-
} else if (!state.settled && !stale(ssStart)) {
|
|
8405
|
-
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
8406
|
-
if (state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
8407
|
-
code = "below_anchored_total";
|
|
8408
|
-
ssStart = state.latestBelow;
|
|
8409
|
-
rsbLength = state.latestBelowRsb;
|
|
8410
|
-
}
|
|
8194
|
+
} else if (!state.settled && !stale(ssStart)) {
|
|
8195
|
+
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
8196
|
+
if (retroactive && state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
8197
|
+
code = "below_anchored_total";
|
|
8198
|
+
ssStart = state.latestBelow;
|
|
8199
|
+
rsbLength = state.latestBelowRsb;
|
|
8411
8200
|
}
|
|
8412
8201
|
}
|
|
8413
|
-
if (code !== void 0) {
|
|
8414
|
-
return {
|
|
8415
|
-
code,
|
|
8416
|
-
layer: state.layer,
|
|
8417
|
-
total: state.total,
|
|
8418
|
-
rsbLength,
|
|
8419
|
-
ssStart,
|
|
8420
|
-
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
8421
|
-
};
|
|
8422
|
-
}
|
|
8423
8202
|
}
|
|
8424
|
-
|
|
8203
|
+
if (code !== void 0) {
|
|
8204
|
+
return {
|
|
8205
|
+
code,
|
|
8206
|
+
layer: state.layer,
|
|
8207
|
+
total: state.total,
|
|
8208
|
+
rsbLength,
|
|
8209
|
+
ssStart,
|
|
8210
|
+
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
8211
|
+
};
|
|
8212
|
+
}
|
|
8425
8213
|
}
|
|
8214
|
+
return void 0;
|
|
8426
8215
|
};
|
|
8427
|
-
_layerNames = new WeakMap();
|
|
8428
|
-
_states = new WeakMap();
|
|
8429
8216
|
|
|
8430
8217
|
// src/mmtp-fec-session.ts
|
|
8431
8218
|
var PATH_BITS = { moq: 1, multicast: 2 };
|
|
@@ -8597,18 +8384,29 @@ var BlockcastMmt = (() => {
|
|
|
8597
8384
|
} catch {
|
|
8598
8385
|
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:malformed");
|
|
8599
8386
|
}
|
|
8600
|
-
const
|
|
8601
|
-
if (!
|
|
8602
|
-
if (
|
|
8603
|
-
|
|
8604
|
-
|
|
8387
|
+
const admission = admitMmtpRepairObject(packet, header, this.symbolSize, this.algorithm);
|
|
8388
|
+
if (!admission) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:malformed");
|
|
8389
|
+
if (!("frame" in admission)) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:length");
|
|
8390
|
+
const frame = admission.frame;
|
|
8391
|
+
if (__privateGet(this, _repairPacketId) !== void 0 && frame.packetId !== __privateGet(this, _repairPacketId)) {
|
|
8605
8392
|
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:foreign-packet-id");
|
|
8606
8393
|
}
|
|
8607
|
-
if (
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8394
|
+
if (admission.reject) {
|
|
8395
|
+
const verdict2 = __privateGet(this, _heldLayers).failsClosed(
|
|
8396
|
+
repairTrackName,
|
|
8397
|
+
frame.ssStart,
|
|
8398
|
+
frame.rsbLength,
|
|
8399
|
+
__privateGet(this, _anchorHorizon),
|
|
8400
|
+
__privateGet(this, _newestSource)
|
|
8401
|
+
);
|
|
8402
|
+
if (verdict2) {
|
|
8403
|
+
__privateGet(this, _stats3).failClosed = verdict2;
|
|
8404
|
+
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:fail-closed");
|
|
8405
|
+
}
|
|
8406
|
+
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, admission.reject === "repairRsId" ? "repair:rs-id" : "repair:scheme-bound");
|
|
8611
8407
|
}
|
|
8408
|
+
__privateGet(this, _repairPacketId) ?? __privateSet(this, _repairPacketId, frame.packetId);
|
|
8409
|
+
if (frame.ssbLength > this.sourceSymbols) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:ssb-length");
|
|
8612
8410
|
const verdict = __privateGet(this, _heldLayers).admit(
|
|
8613
8411
|
repairTrackName,
|
|
8614
8412
|
frame.ssStart,
|
|
@@ -8895,8 +8693,8 @@ var BlockcastMmt = (() => {
|
|
|
8895
8693
|
}
|
|
8896
8694
|
var KEYFRAME_FLAGS = 33554432;
|
|
8897
8695
|
var DELTA_FLAGS = 16842752;
|
|
8898
|
-
var
|
|
8899
|
-
var MAX_TRACK_ID =
|
|
8696
|
+
var UINT32_MAX = 4294967295;
|
|
8697
|
+
var MAX_TRACK_ID = UINT32_MAX - 1;
|
|
8900
8698
|
var INT32_MIN = -2147483648;
|
|
8901
8699
|
var INT32_MAX = 2147483647;
|
|
8902
8700
|
function assertTrackId(trackId, label) {
|
|
@@ -8905,8 +8703,8 @@ var BlockcastMmt = (() => {
|
|
|
8905
8703
|
}
|
|
8906
8704
|
}
|
|
8907
8705
|
function assertTimescale(timescale, label) {
|
|
8908
|
-
if (!Number.isInteger(timescale) || timescale <= 0 || timescale >
|
|
8909
|
-
throw new RangeError(`${label} must be an integer in 1..${
|
|
8706
|
+
if (!Number.isInteger(timescale) || timescale <= 0 || timescale > UINT32_MAX) {
|
|
8707
|
+
throw new RangeError(`${label} must be an integer in 1..${UINT32_MAX}, got ${timescale}`);
|
|
8910
8708
|
}
|
|
8911
8709
|
}
|
|
8912
8710
|
function assertCompositionTimeOffsets(samples) {
|
|
@@ -10237,6 +10035,11 @@ var BlockcastMmt = (() => {
|
|
|
10237
10035
|
const [leftText, rightText = ""] = address.split("::");
|
|
10238
10036
|
const left = leftText ? leftText.split(":") : [];
|
|
10239
10037
|
const right = rightText ? rightText.split(":") : [];
|
|
10038
|
+
const tail = compressed ? right : left;
|
|
10039
|
+
if (tail.length > 0 && tail[tail.length - 1].includes(".")) {
|
|
10040
|
+
const v4 = parseIpv4(tail.pop());
|
|
10041
|
+
tail.push((v4[0] << 8 | v4[1]).toString(16), (v4[2] << 8 | v4[3]).toString(16));
|
|
10042
|
+
}
|
|
10240
10043
|
if (left.some((group) => group.includes(".")) || right.some((group) => group.includes("."))) {
|
|
10241
10044
|
throw new Error(`invalid IPv6 address ${address}`);
|
|
10242
10045
|
}
|
|
@@ -10259,7 +10062,7 @@ var BlockcastMmt = (() => {
|
|
|
10259
10062
|
return parseIpv4(address);
|
|
10260
10063
|
}
|
|
10261
10064
|
function endpointBytes(endpoint) {
|
|
10262
|
-
if (!validPort(endpoint.port) || !
|
|
10065
|
+
if (!validPort(endpoint.port) || !validU32(endpoint.streamId)) {
|
|
10263
10066
|
throw new Error("invalid BCPV2 endpoint");
|
|
10264
10067
|
}
|
|
10265
10068
|
const source = parseBcProvenanceV2Address(endpoint.sourceAddress);
|
|
@@ -10319,7 +10122,7 @@ var BlockcastMmt = (() => {
|
|
|
10319
10122
|
offset += source.byteLength;
|
|
10320
10123
|
prefix.set(group, offset);
|
|
10321
10124
|
offset += group.byteLength;
|
|
10322
|
-
prefix.set(u16(endpoint.
|
|
10125
|
+
prefix.set(u16(endpoint.port), offset);
|
|
10323
10126
|
const message = new Uint8Array(prefix.byteLength + record.body.byteLength);
|
|
10324
10127
|
message.set(prefix);
|
|
10325
10128
|
message.set(record.body, prefix.byteLength);
|
|
@@ -11830,7 +11633,6 @@ var BlockcastMmt = (() => {
|
|
|
11830
11633
|
var BCPV2_LEAD_BOUND_MS = 5e3;
|
|
11831
11634
|
var REPLAY_WINDOW = 64n;
|
|
11832
11635
|
var BUCKET_DEPTH = 64;
|
|
11833
|
-
var BUCKET_RATE_PER_MS = 1;
|
|
11834
11636
|
var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
11835
11637
|
function sameBytes(a, b) {
|
|
11836
11638
|
if (a.byteLength !== b.byteLength)
|
|
@@ -11860,6 +11662,10 @@ var BlockcastMmt = (() => {
|
|
|
11860
11662
|
__publicField(this, "verifySig");
|
|
11861
11663
|
__publicField(this, "accepted");
|
|
11862
11664
|
__publicField(this, "queueLimit");
|
|
11665
|
+
/** Octets a held datagram is charged beyond its payload: the IPv4 or IPv6 plus UDP headers (step 2). */
|
|
11666
|
+
__publicField(this, "headerOctets");
|
|
11667
|
+
/** Budget refill, tokens per ms: V / 1000 (step 1). */
|
|
11668
|
+
__publicField(this, "bucketRatePerMs");
|
|
11863
11669
|
__publicField(this, "current", null);
|
|
11864
11670
|
__publicField(this, "previous", null);
|
|
11865
11671
|
__publicField(this, "graceStart", {});
|
|
@@ -11874,10 +11680,12 @@ var BlockcastMmt = (() => {
|
|
|
11874
11680
|
__publicField(this, "holdBytes", { multicast: 0, unicast: 0 });
|
|
11875
11681
|
__publicField(this, "order", 0);
|
|
11876
11682
|
__publicField(this, "buckets");
|
|
11877
|
-
|
|
11878
|
-
this.
|
|
11879
|
-
|
|
11880
|
-
|
|
11683
|
+
const delay = cfg.maxAuthDelayMs;
|
|
11684
|
+
this.maxAuthDelayMs = typeof delay !== "number" || !Number.isFinite(delay) ? 200 : delay < 50 ? 50 : delay > 1e3 ? 1e3 : Number.isInteger(delay) ? delay : 200;
|
|
11685
|
+
const timer = cfg.manifestTimerMs;
|
|
11686
|
+
this.manifestTimerMs = typeof timer !== "number" || !Number.isFinite(timer) ? 20 : timer < 1 || Number.isInteger(timer) ? timer : 20;
|
|
11687
|
+
if (!(this.manifestTimerMs >= 1)) {
|
|
11688
|
+
throw new Error("BCPV2 endpoint ignored: manifestTimerMs below 1");
|
|
11881
11689
|
}
|
|
11882
11690
|
if (!(cfg.bandwidth > 0))
|
|
11883
11691
|
throw new Error("BCPV2 endpoint ignored: protected endpoint without bandwidth");
|
|
@@ -11902,6 +11710,8 @@ var BlockcastMmt = (() => {
|
|
|
11902
11710
|
multicast: this.maxAuthDelayMs * bandwidth / 8e3,
|
|
11903
11711
|
unicast: (this.maxAuthDelayMs + this.widenMs) * bandwidth / 8e3
|
|
11904
11712
|
};
|
|
11713
|
+
this.headerOctets = parseBcProvenanceV2Address(cfg.endpoint.groupAddress).byteLength === 16 ? 48 : 28;
|
|
11714
|
+
this.bucketRatePerMs = Math.max(1e3, 1.25 * bandwidth / (8 * 512 * 37)) / 1e3;
|
|
11905
11715
|
const t = this.now();
|
|
11906
11716
|
this.buckets = {
|
|
11907
11717
|
multicast: { tokens: BUCKET_DEPTH, last: t, dropped: 0 },
|
|
@@ -11917,7 +11727,7 @@ var BlockcastMmt = (() => {
|
|
|
11917
11727
|
return 0;
|
|
11918
11728
|
return this.lead ?? BCPV2_LEAD_BOUND_MS;
|
|
11919
11729
|
}
|
|
11920
|
-
/** Step 1: a record datagram on `
|
|
11730
|
+
/** Step 1: a record datagram on `port`, or a record object of the manifest track. */
|
|
11921
11731
|
onRecord(bytes, path) {
|
|
11922
11732
|
var _a;
|
|
11923
11733
|
const now = this.now();
|
|
@@ -12001,7 +11811,7 @@ var BlockcastMmt = (() => {
|
|
|
12001
11811
|
arrival: now,
|
|
12002
11812
|
sent,
|
|
12003
11813
|
digest: hex(sha2562(bytes)),
|
|
12004
|
-
pairs: record.digests.map((d, i) => ({ digest: hex(d), seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
11814
|
+
pairs: record.digests.map((d, i) => ({ digest: hex(d), epoch, seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
12005
11815
|
awaitingMulticast: path === "mirror"
|
|
12006
11816
|
};
|
|
12007
11817
|
this.verified.set(id, entry);
|
|
@@ -12068,12 +11878,12 @@ var BlockcastMmt = (() => {
|
|
|
12068
11878
|
digest(datagram) {
|
|
12069
11879
|
return hex(bcProvenanceV2DatagramDigest(this.cfg.endpoint, datagram));
|
|
12070
11880
|
}
|
|
12071
|
-
/** Consume the
|
|
11881
|
+
/** Consume the pending entry for `digest` lowest in (epoch, sequence) order, admitting it. */
|
|
12072
11882
|
consumePending(digest, now) {
|
|
12073
11883
|
const list = this.pending.get(digest);
|
|
12074
11884
|
if (!list?.length)
|
|
12075
11885
|
return false;
|
|
12076
|
-
list.sort((a, b) => a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0);
|
|
11886
|
+
list.sort((a, b) => a.pair.epoch - b.pair.epoch || (a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0));
|
|
12077
11887
|
const hit = list.shift();
|
|
12078
11888
|
if (list.length === 0)
|
|
12079
11889
|
this.pending.delete(digest);
|
|
@@ -12082,14 +11892,18 @@ var BlockcastMmt = (() => {
|
|
|
12082
11892
|
this.admittedUntil.set(digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
12083
11893
|
return true;
|
|
12084
11894
|
}
|
|
11895
|
+
/** A held datagram's charge against its queue: payload plus IP and UDP headers. */
|
|
11896
|
+
charge(held) {
|
|
11897
|
+
return held.datagram.byteLength + this.headerOctets;
|
|
11898
|
+
}
|
|
12085
11899
|
enqueue(held, path, now) {
|
|
12086
|
-
if (this.holdBytes[path] + held
|
|
11900
|
+
if (this.holdBytes[path] + this.charge(held) > this.queueLimit[path]) {
|
|
12087
11901
|
return { verdict: "overflow", admitted: [] };
|
|
12088
11902
|
}
|
|
12089
11903
|
held.expiry = now + this.maxAuthDelayMs + (path === "unicast" ? this.widenMs : 0);
|
|
12090
11904
|
held.order = this.order++;
|
|
12091
11905
|
this.hold[path].push(held);
|
|
12092
|
-
this.holdBytes[path] += held
|
|
11906
|
+
this.holdBytes[path] += this.charge(held);
|
|
12093
11907
|
return { verdict: "held", admitted: [] };
|
|
12094
11908
|
}
|
|
12095
11909
|
/** Remove and return the oldest held datagram with `digest`, across both queues. */
|
|
@@ -12104,7 +11918,7 @@ var BlockcastMmt = (() => {
|
|
|
12104
11918
|
if (!best)
|
|
12105
11919
|
return void 0;
|
|
12106
11920
|
const [held] = this.hold[best.path].splice(best.index, 1);
|
|
12107
|
-
this.holdBytes[best.path] -= held
|
|
11921
|
+
this.holdBytes[best.path] -= this.charge(held);
|
|
12108
11922
|
return held.datagram;
|
|
12109
11923
|
}
|
|
12110
11924
|
dropAdmittedHeld() {
|
|
@@ -12112,7 +11926,7 @@ var BlockcastMmt = (() => {
|
|
|
12112
11926
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
12113
11927
|
const keep = h.digest === void 0 || !this.admittedUntil.has(h.digest);
|
|
12114
11928
|
if (!keep)
|
|
12115
|
-
this.holdBytes[path] -= h
|
|
11929
|
+
this.holdBytes[path] -= this.charge(h);
|
|
12116
11930
|
return keep;
|
|
12117
11931
|
});
|
|
12118
11932
|
}
|
|
@@ -12197,7 +12011,7 @@ var BlockcastMmt = (() => {
|
|
|
12197
12011
|
}
|
|
12198
12012
|
take(path, now) {
|
|
12199
12013
|
const b = this.buckets[path];
|
|
12200
|
-
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) *
|
|
12014
|
+
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) * this.bucketRatePerMs);
|
|
12201
12015
|
b.last = now;
|
|
12202
12016
|
if (b.tokens < 1) {
|
|
12203
12017
|
b.dropped++;
|
|
@@ -12211,7 +12025,7 @@ var BlockcastMmt = (() => {
|
|
|
12211
12025
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
12212
12026
|
if (h.expiry > now)
|
|
12213
12027
|
return true;
|
|
12214
|
-
this.holdBytes[path] -= h
|
|
12028
|
+
this.holdBytes[path] -= this.charge(h);
|
|
12215
12029
|
this.expired[path]++;
|
|
12216
12030
|
return false;
|
|
12217
12031
|
});
|
|
@@ -12243,7 +12057,7 @@ var BlockcastMmt = (() => {
|
|
|
12243
12057
|
}
|
|
12244
12058
|
/**
|
|
12245
12059
|
* Retire verified entries below their arrival path's replay floor, keeping a mirrored entry
|
|
12246
|
-
* while the freshness check would still pass its copy on `
|
|
12060
|
+
* while the freshness check would still pass its copy on `port`.
|
|
12247
12061
|
* ponytail: linear scan per record; index by (kid, epoch) if retained sets grow past a few thousand.
|
|
12248
12062
|
*/
|
|
12249
12063
|
prune(now) {
|