@blockcast/mmt-container 0.4.1 → 0.5.0-main.2c1f81817995
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 +2 -2
- package/dist/browser-runtime.js +3458 -356
- package/dist/browser-runtime.js.map +4 -4
- package/dist/cmcd-producer.d.ts +5 -1
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js +18 -2
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/fec-manager.d.ts +21 -16
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +177 -80
- package/dist/fec-manager.js.map +3 -3
- package/dist/held-layer-admission.d.ts +58 -0
- package/dist/held-layer-admission.d.ts.map +1 -0
- package/dist/held-layer-admission.js +105 -0
- package/dist/held-layer-admission.js.map +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +2682 -1023
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-fec-session.d.ts +36 -3
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +114 -5
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +72 -28
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +321 -203
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-auth-catalog.d.ts +10 -2
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +422 -22
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-manager.d.ts +48 -9
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +3406 -340
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +4 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +20 -3
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.d.ts +1 -2
- package/dist/no-modules.d.ts.map +1 -1
- package/dist/no-modules.js +2668 -1007
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.d.ts +10 -1
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +5 -2
- package/dist/repair-fetch.js.map +2 -2
- package/package.json +5 -14
- package/src/cmcd-producer.ts +6 -1
- package/src/fec-manager.ts +121 -131
- package/src/held-layer-admission.ts +161 -0
- package/src/index.ts +5 -9
- package/src/mmtp-fec-session.ts +76 -3
- package/src/mmtp-router.ts +390 -272
- package/src/multicast-auth-catalog.ts +81 -8
- package/src/multicast-manager.ts +189 -48
- package/src/multicast-transport.ts +6 -2
- package/src/no-modules.ts +1 -6
- package/src/repair-fetch.ts +24 -4
- package/dist/manifest-auth-plan.d.ts +0 -31
- package/dist/manifest-auth-plan.d.ts.map +0 -1
- package/dist/manifest-auth-plan.js +0 -426
- package/dist/manifest-auth-plan.js.map +0 -7
- package/dist/source-auth.d.ts +0 -2
- package/dist/source-auth.d.ts.map +0 -1
- package/dist/source-auth.js +0 -3912
- package/dist/source-auth.js.map +0 -7
- package/src/manifest-auth-plan.ts +0 -55
- package/src/source-auth.ts +0 -1
- package/test-vectors/generate-source-auth-fixture.mjs +0 -114
- package/test-vectors/source-auth-v1.json +0 -61
package/dist/browser-runtime.js
CHANGED
|
@@ -5066,18 +5066,18 @@ function hevcNalUnitsContainIrap(nals) {
|
|
|
5066
5066
|
return sawNal ? false : void 0;
|
|
5067
5067
|
}
|
|
5068
5068
|
function classifyHevcKeyframe(data) {
|
|
5069
|
-
const
|
|
5070
|
-
return
|
|
5069
|
+
const split2 = splitHevcSample(data);
|
|
5070
|
+
return split2 === null ? void 0 : hevcNalUnitsContainIrap(split2.nals);
|
|
5071
5071
|
}
|
|
5072
5072
|
function detectHevcKeyframe(data) {
|
|
5073
5073
|
return classifyHevcKeyframe(data) === true;
|
|
5074
5074
|
}
|
|
5075
5075
|
function prepareHevcCmafSample(data, sampleEntry) {
|
|
5076
|
-
const
|
|
5077
|
-
if (
|
|
5078
|
-
const keyframe = hevcNalUnitsContainIrap(
|
|
5076
|
+
const split2 = splitHevcSample(data);
|
|
5077
|
+
if (split2 === null) return null;
|
|
5078
|
+
const keyframe = hevcNalUnitsContainIrap(split2.nals);
|
|
5079
5079
|
if (keyframe === void 0) return null;
|
|
5080
|
-
const normalized = hevcNalUnitsToLengthPrefixed(data,
|
|
5080
|
+
const normalized = hevcNalUnitsToLengthPrefixed(data, split2.nals, split2.lengthPrefixed, sampleEntry);
|
|
5081
5081
|
return normalized === null ? null : { keyframe, data: normalized };
|
|
5082
5082
|
}
|
|
5083
5083
|
function normalizeHevcSampleToLengthPrefixed(data, sampleEntry) {
|
|
@@ -7566,14 +7566,22 @@ function toWindowMulticastSubscribeConfig(input) {
|
|
|
7566
7566
|
if (input.packetIds?.some((id) => !isMulticastTrackPacketId(id)) || input.tracks?.some((track) => !isMulticastTrackPacketId(track.packetId))) {
|
|
7567
7567
|
throw new RangeError(`multicast packetId must be an integer from 1 to ${MMTP_PACKET_ID_MAX2}; packetId 0 is the MMTP signaling flow and is never a track's`);
|
|
7568
7568
|
}
|
|
7569
|
+
const source = input.source?.trim() || void 0;
|
|
7570
|
+
const senderAddress = input.senderAddress?.trim() || void 0;
|
|
7571
|
+
if (source && senderAddress) {
|
|
7572
|
+
throw new TypeError("multicast senderAddress is an ASM filter and never appears beside source");
|
|
7573
|
+
}
|
|
7569
7574
|
const base = {
|
|
7570
7575
|
group: input.group,
|
|
7571
7576
|
port: input.port,
|
|
7572
|
-
...
|
|
7577
|
+
...source ? { source } : {},
|
|
7573
7578
|
consume: "bytes",
|
|
7574
7579
|
...input.packetIds?.length ? { packetIds: [...input.packetIds] } : {}
|
|
7575
7580
|
};
|
|
7581
|
+
const filter = senderAddress ? { senderAddress } : {};
|
|
7576
7582
|
if (input.atsc3) {
|
|
7583
|
+
if (senderAddress)
|
|
7584
|
+
throw new TypeError("an ATSC3 tuner request carries no senderAddress");
|
|
7577
7585
|
const { frequency, plpId, serviceId } = input.atsc3;
|
|
7578
7586
|
if (!Number.isInteger(frequency) || frequency < 1) {
|
|
7579
7587
|
throw new TypeError(`ATSC3 tuning requires a positive integer frequency in kHz, got ${String(frequency)}`);
|
|
@@ -7593,6 +7601,7 @@ function toWindowMulticastSubscribeConfig(input) {
|
|
|
7593
7601
|
if (!wantsAmt) {
|
|
7594
7602
|
const native = {
|
|
7595
7603
|
...base,
|
|
7604
|
+
...filter,
|
|
7596
7605
|
origin: "ip-multicast",
|
|
7597
7606
|
...input.tracks?.length ? { tracks: input.tracks.map(({ name, packetId }) => ({ name, packetId })) } : {}
|
|
7598
7607
|
};
|
|
@@ -7618,6 +7627,7 @@ function toWindowMulticastSubscribeConfig(input) {
|
|
|
7618
7627
|
}
|
|
7619
7628
|
return {
|
|
7620
7629
|
...base,
|
|
7630
|
+
...filter,
|
|
7621
7631
|
origin: "amt",
|
|
7622
7632
|
relay,
|
|
7623
7633
|
relayPort
|
|
@@ -7643,6 +7653,9 @@ function isValidMulticastGroupAddress(address) {
|
|
|
7643
7653
|
function normalizeMulticastEndpoint(endpoint) {
|
|
7644
7654
|
const groupAddress = endpoint.groupAddress.trim().toLowerCase();
|
|
7645
7655
|
const sourceAddress = endpoint.sourceAddress?.trim().toLowerCase() || void 0;
|
|
7656
|
+
const senderAddress = endpoint.senderAddress?.trim().toLowerCase() || void 0;
|
|
7657
|
+
if (sourceAddress && senderAddress)
|
|
7658
|
+
return void 0;
|
|
7646
7659
|
const amtRelay = endpoint.amtRelay?.trim().toLowerCase() || void 0;
|
|
7647
7660
|
if (!isValidMulticastGroupAddress(groupAddress) || !isPositivePort(endpoint.port))
|
|
7648
7661
|
return void 0;
|
|
@@ -7653,6 +7666,7 @@ function normalizeMulticastEndpoint(endpoint) {
|
|
|
7653
7666
|
return {
|
|
7654
7667
|
groupAddress,
|
|
7655
7668
|
...sourceAddress ? { sourceAddress } : {},
|
|
7669
|
+
...senderAddress ? { senderAddress } : {},
|
|
7656
7670
|
port: endpoint.port,
|
|
7657
7671
|
...endpoint.mode ? { mode: endpoint.mode } : {},
|
|
7658
7672
|
...amtRelay ? { amtRelay } : {},
|
|
@@ -7672,7 +7686,9 @@ function multicastEndpointKey(endpoint) {
|
|
|
7672
7686
|
normalized.amtRelay ?? "",
|
|
7673
7687
|
normalized.amtRelayPort ?? "",
|
|
7674
7688
|
// A DRIAD join is a different gateway subscription from a native one.
|
|
7675
|
-
...normalized.amtDiscovery ? [normalized.amtDiscovery] : []
|
|
7689
|
+
...normalized.amtDiscovery ? [normalized.amtDiscovery] : [],
|
|
7690
|
+
// An ASM sender filter is a different subscription from the unfiltered join.
|
|
7691
|
+
...normalized.senderAddress ? [{ senderAddress: normalized.senderAddress }] : []
|
|
7676
7692
|
]);
|
|
7677
7693
|
}
|
|
7678
7694
|
function isMulticastTrackPacketId(value) {
|
|
@@ -8930,14 +8946,14 @@ function validateNetworkSourceEntry(s, i) {
|
|
|
8930
8946
|
}
|
|
8931
8947
|
return;
|
|
8932
8948
|
}
|
|
8933
|
-
const
|
|
8934
|
-
if (!
|
|
8949
|
+
const inRange2 = (v, min, max) => Number.isInteger(v) && v >= min && v <= max;
|
|
8950
|
+
if (!inRange2(s.frequency, 1, Number.MAX_SAFE_INTEGER)) {
|
|
8935
8951
|
throw err("invalid_network_source", `frequency must be a positive integer in kHz; got ${s.frequency}`);
|
|
8936
8952
|
}
|
|
8937
|
-
if (s.plpId !== void 0 && !
|
|
8953
|
+
if (s.plpId !== void 0 && !inRange2(s.plpId, 0, 63)) {
|
|
8938
8954
|
throw err("invalid_network_source", `plpId must be 0..63; got ${s.plpId}`);
|
|
8939
8955
|
}
|
|
8940
|
-
if (s.serviceId !== void 0 && !
|
|
8956
|
+
if (s.serviceId !== void 0 && !inRange2(s.serviceId, 0, 65535)) {
|
|
8941
8957
|
throw err("invalid_network_source", `serviceId must be 0..65535; got ${s.serviceId}`);
|
|
8942
8958
|
}
|
|
8943
8959
|
if (s.slsUri !== void 0 && (typeof s.slsUri !== "string" || !isAbsoluteUri(s.slsUri))) {
|
|
@@ -10647,9 +10663,9 @@ var CmafBlockAssembler = class {
|
|
|
10647
10663
|
const sample = splitH264SampleNalUnits(data);
|
|
10648
10664
|
const nalTypes = describeH264NalTypes(sample.nals);
|
|
10649
10665
|
const classify = classifyH264Keyframe(data);
|
|
10650
|
-
const
|
|
10666
|
+
const hex3 = Array.from(data.subarray(0, Math.min(48, data.length))).map((b) => b.toString(16).padStart(2, "0")).join(" ");
|
|
10651
10667
|
console.log(
|
|
10652
|
-
`[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} metadataKf=${metadataKeyframe} classify=${classify} format=${sample.format} nalTypes=[${nalTypes.join(",")}] first48=[${
|
|
10668
|
+
`[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} metadataKf=${metadataKeyframe} classify=${classify} format=${sample.format} nalTypes=[${nalTypes.join(",")}] first48=[${hex3}]`
|
|
10653
10669
|
);
|
|
10654
10670
|
}
|
|
10655
10671
|
/** Mark video as already synchronized by an external decoder path.
|
|
@@ -13257,6 +13273,79 @@ function decideGap(input) {
|
|
|
13257
13273
|
});
|
|
13258
13274
|
}
|
|
13259
13275
|
|
|
13276
|
+
// src/held-layer-admission.ts
|
|
13277
|
+
var HORIZON_CAP = 1073741824;
|
|
13278
|
+
function heldLayerAnchorHorizon(retentionBlocks, k) {
|
|
13279
|
+
return retentionBlocks === void 0 || k <= 0 ? HORIZON_CAP : Math.min(HORIZON_CAP, retentionBlocks * k);
|
|
13280
|
+
}
|
|
13281
|
+
var HeldLayerAdmission = class {
|
|
13282
|
+
#layerNames;
|
|
13283
|
+
#states;
|
|
13284
|
+
constructor(layers, heldLayers = [0]) {
|
|
13285
|
+
const held = new Set(heldLayers);
|
|
13286
|
+
this.#layerNames = new Set(layers.map((l) => l.repairTrackName));
|
|
13287
|
+
this.#states = layers.filter((l) => held.has(l.layer)).map((l) => ({ layer: l.layer, repairTrackName: l.repairTrackName, total: l.rsIdOffset + l.repairSymbols }));
|
|
13288
|
+
}
|
|
13289
|
+
/**
|
|
13290
|
+
* Judges one repair packet, after the 4.5 discards and the size check and
|
|
13291
|
+
* before it places a block. `repairTrackName` is the subscribed repair
|
|
13292
|
+
* track, never inferred from RS_ID; without it only (b) runs. A packet on a
|
|
13293
|
+
* repair track the catalog does not list is `'discard'` and anchors nothing.
|
|
13294
|
+
* `sourceFrontier` is the newest source SS_ID, if any: a repair more than
|
|
13295
|
+
* `horizon` past it is judged without moving the anchored state.
|
|
13296
|
+
*
|
|
13297
|
+
* @returns undefined to admit, `'discard'`, or the fail-closed reason.
|
|
13298
|
+
*/
|
|
13299
|
+
admit(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier) {
|
|
13300
|
+
if (repairTrackName !== void 0 && !this.#layerNames.has(repairTrackName)) return "discard";
|
|
13301
|
+
const farAhead = sourceFrontier !== void 0 && serial32Precedes(sourceFrontier, ssStart) && serial32Distance(sourceFrontier, ssStart) > horizon;
|
|
13302
|
+
for (const state of this.#states) {
|
|
13303
|
+
let code;
|
|
13304
|
+
if (farAhead) {
|
|
13305
|
+
if (rsbLength < state.total) {
|
|
13306
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
13307
|
+
else if (state.anchor !== void 0 || state.settled) code = "below_anchored_total";
|
|
13308
|
+
}
|
|
13309
|
+
} else {
|
|
13310
|
+
if (state.newest === void 0 || serial32Precedes(state.newest, ssStart)) state.newest = ssStart;
|
|
13311
|
+
const stale = (s) => serial32Distance(s, state.newest) > horizon;
|
|
13312
|
+
if (state.latestBelow !== void 0 && stale(state.latestBelow)) {
|
|
13313
|
+
delete state.latestBelow;
|
|
13314
|
+
delete state.latestBelowRsb;
|
|
13315
|
+
}
|
|
13316
|
+
if (state.anchor !== void 0 && stale(state.anchor)) state.settled = true;
|
|
13317
|
+
if (rsbLength < state.total) {
|
|
13318
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
13319
|
+
else if (stale(ssStart)) continue;
|
|
13320
|
+
else if (state.settled || state.anchor !== void 0 && !serial32Precedes(ssStart, state.anchor)) code = "below_anchored_total";
|
|
13321
|
+
else if (state.latestBelow === void 0 || serial32Precedes(state.latestBelow, ssStart)) {
|
|
13322
|
+
state.latestBelow = ssStart;
|
|
13323
|
+
state.latestBelowRsb = rsbLength;
|
|
13324
|
+
}
|
|
13325
|
+
} else if (!state.settled && !stale(ssStart)) {
|
|
13326
|
+
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
13327
|
+
if (state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
13328
|
+
code = "below_anchored_total";
|
|
13329
|
+
ssStart = state.latestBelow;
|
|
13330
|
+
rsbLength = state.latestBelowRsb;
|
|
13331
|
+
}
|
|
13332
|
+
}
|
|
13333
|
+
}
|
|
13334
|
+
if (code !== void 0) {
|
|
13335
|
+
return {
|
|
13336
|
+
code,
|
|
13337
|
+
layer: state.layer,
|
|
13338
|
+
total: state.total,
|
|
13339
|
+
rsbLength,
|
|
13340
|
+
ssStart,
|
|
13341
|
+
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
13342
|
+
};
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13345
|
+
return void 0;
|
|
13346
|
+
}
|
|
13347
|
+
};
|
|
13348
|
+
|
|
13260
13349
|
// src/fec-manager.ts
|
|
13261
13350
|
var RS_MAX_BLOCK_SYMBOLS = 255;
|
|
13262
13351
|
var WORKER_ERROR_LOG_CAP = 5;
|
|
@@ -13413,6 +13502,9 @@ var FECManager = class _FECManager {
|
|
|
13413
13502
|
// Receiver-local first-accepted-symbol time per block, bounded like
|
|
13414
13503
|
// #blockPathCounts (see blockAnchorAtMs).
|
|
13415
13504
|
this.#blockAnchorsMs = /* @__PURE__ */ new Map();
|
|
13505
|
+
// The receipt time of the packet being fed, when it is not now: a datagram
|
|
13506
|
+
// held on a miss and admitted later (withReceiptTime).
|
|
13507
|
+
this.#receiptAtMs = void 0;
|
|
13416
13508
|
// Exact catalog identity observed on accepted symbols for each FEC block.
|
|
13417
13509
|
// Conflicting identities collapse to null so recovery fails closed.
|
|
13418
13510
|
this.#blockCatalogTrackNames = /* @__PURE__ */ new Map();
|
|
@@ -13460,6 +13552,9 @@ var FECManager = class _FECManager {
|
|
|
13460
13552
|
// Worker recovery emits all K source symbols. Track recovery once per block
|
|
13461
13553
|
// while routing only ESI slots that were absent from the direct source path.
|
|
13462
13554
|
this.#workerRecoveredBlocks = /* @__PURE__ */ new Set();
|
|
13555
|
+
// Worker-recovered blocks a recovered packet of which admission refused,
|
|
13556
|
+
// until the decode's final symbol (7.2.7: the block is unrecovered).
|
|
13557
|
+
this.#refusedRecoveries = /* @__PURE__ */ new Set();
|
|
13463
13558
|
// How the multicast leg delivers symbols (see MulticastSymbolDelivery). The
|
|
13464
13559
|
// provider declares it per transport join; absence keeps datagram semantics.
|
|
13465
13560
|
this.#multicastSymbolDelivery = "datagram";
|
|
@@ -13598,6 +13693,11 @@ var FECManager = class _FECManager {
|
|
|
13598
13693
|
/** A stream's SBN went backwards (or jumped implausibly): the source restarted. */
|
|
13599
13694
|
this.onSourceDiscontinuity = null;
|
|
13600
13695
|
// Worker-path callbacks — async recovery and stats from FecWorkerClient.
|
|
13696
|
+
/**
|
|
13697
|
+
* A recovered source symbol. Return false when admission refused the
|
|
13698
|
+
* packet it carries (bc-provenance v2, draft-ramadan-moq-multicast-01
|
|
13699
|
+
* 7.2.7): the block is then unrecovered and stays open to its FEC deadline.
|
|
13700
|
+
*/
|
|
13601
13701
|
this.onBlockRecovered = null;
|
|
13602
13702
|
this.onFecStatsUpdate = null;
|
|
13603
13703
|
}
|
|
@@ -13650,6 +13750,7 @@ var FECManager = class _FECManager {
|
|
|
13650
13750
|
#seenSymbols;
|
|
13651
13751
|
#blockPathCounts;
|
|
13652
13752
|
#blockAnchorsMs;
|
|
13753
|
+
#receiptAtMs;
|
|
13653
13754
|
#blockCatalogTrackNames;
|
|
13654
13755
|
#startupAlignedPackets;
|
|
13655
13756
|
#startupPartialBlocks;
|
|
@@ -13662,6 +13763,7 @@ var FECManager = class _FECManager {
|
|
|
13662
13763
|
#repairDeadlines;
|
|
13663
13764
|
#repairDeadlineAtMs;
|
|
13664
13765
|
#workerRecoveredBlocks;
|
|
13766
|
+
#refusedRecoveries;
|
|
13665
13767
|
#multicastSymbolDelivery;
|
|
13666
13768
|
#orderedFrontiers;
|
|
13667
13769
|
#orderedPendingBlocks;
|
|
@@ -13771,12 +13873,12 @@ var FECManager = class _FECManager {
|
|
|
13771
13873
|
* still be held behind it. Retirement is swept lazily here, on the stream's
|
|
13772
13874
|
* own terminal events, so it needs no timer.
|
|
13773
13875
|
*/
|
|
13774
|
-
#markTerminal(key, sbn, packetId,
|
|
13876
|
+
#markTerminal(key, sbn, packetId, clean2) {
|
|
13775
13877
|
const nowMs = performance.now();
|
|
13776
13878
|
this.#retireTerminalBlocks(packetId, nowMs);
|
|
13777
13879
|
this.#forgetOrderedPending(key);
|
|
13778
13880
|
this.#completedBlocks.add(key);
|
|
13779
|
-
if (
|
|
13881
|
+
if (clean2) this.#cleanlyCompletedBlocks.add(key);
|
|
13780
13882
|
this.#indexBlockKey(key, sbn, packetId);
|
|
13781
13883
|
const config = this.#trackConfig(packetId);
|
|
13782
13884
|
const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
@@ -13801,6 +13903,7 @@ var FECManager = class _FECManager {
|
|
|
13801
13903
|
this.#completedBlocks.delete(record.key);
|
|
13802
13904
|
this.#cleanlyCompletedBlocks.delete(record.key);
|
|
13803
13905
|
this.#workerRecoveredBlocks.delete(record.key);
|
|
13906
|
+
this.#refusedRecoveries.delete(record.key);
|
|
13804
13907
|
this.#failedBlocks.delete(record.key);
|
|
13805
13908
|
this.#resolvedBlockClasses.delete(record.key);
|
|
13806
13909
|
this.#repairedBlocks.delete(record.key);
|
|
@@ -14139,7 +14242,7 @@ var FECManager = class _FECManager {
|
|
|
14139
14242
|
* remainder, so repeated source progress costs no timer operation.
|
|
14140
14243
|
*/
|
|
14141
14244
|
#armRepairDeadline(key, sbn, packetId, catalogTrackName, delayMs) {
|
|
14142
|
-
const deadlineMs =
|
|
14245
|
+
const deadlineMs = this.#notEarlierUnderReceipt(this.#receiptNow() + delayMs, this.#repairDeadlineAtMs.get(key));
|
|
14143
14246
|
this.#repairDeadlineAtMs.set(key, deadlineMs);
|
|
14144
14247
|
const armed = this.#repairDeadlines.get(key);
|
|
14145
14248
|
if (armed?.handle != null) {
|
|
@@ -14159,7 +14262,7 @@ var FECManager = class _FECManager {
|
|
|
14159
14262
|
timer.handle = null;
|
|
14160
14263
|
this.#requestRepair(sbn, packetId, catalogTrackName);
|
|
14161
14264
|
};
|
|
14162
|
-
timer.handle = setTimeout(onFire,
|
|
14265
|
+
timer.handle = setTimeout(onFire, Math.max(0, deadlineMs - performance.now()));
|
|
14163
14266
|
this.#repairDeadlines.set(key, timer);
|
|
14164
14267
|
}
|
|
14165
14268
|
#requestRepair(sbn, packetId, catalogTrackName, settledByOrder = false) {
|
|
@@ -14563,7 +14666,10 @@ var FECManager = class _FECManager {
|
|
|
14563
14666
|
}
|
|
14564
14667
|
#armBlockTimeout(sbn, packetId, catalogTrackName, delayMs) {
|
|
14565
14668
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
14566
|
-
const waitAtMs =
|
|
14669
|
+
const waitAtMs = this.#notEarlierUnderReceipt(
|
|
14670
|
+
this.#receiptNow() + Math.max(1, Math.ceil(delayMs)),
|
|
14671
|
+
this.#blockWaitDeadlinesMs.get(key)
|
|
14672
|
+
);
|
|
14567
14673
|
const holdAtMs = this.#repairHoldUntilMs.get(key);
|
|
14568
14674
|
this.#blockWaitDeadlinesMs.set(key, holdAtMs === void 0 ? waitAtMs : Math.min(waitAtMs, holdAtMs));
|
|
14569
14675
|
this.#applyBlockDeadline(key, sbn, packetId, catalogTrackName);
|
|
@@ -14952,6 +15058,7 @@ var FECManager = class _FECManager {
|
|
|
14952
15058
|
resetSet(this.#cleanlyCompletedBlocks);
|
|
14953
15059
|
resetSet(this.#repairRequestedBlocks);
|
|
14954
15060
|
resetSet(this.#workerRecoveredBlocks);
|
|
15061
|
+
resetSet(this.#refusedRecoveries);
|
|
14955
15062
|
resetSet(this.#startupAlignedPackets);
|
|
14956
15063
|
resetSet(this.#startupPartialBlocks);
|
|
14957
15064
|
resetSet(this.#startupRepairReadyPackets);
|
|
@@ -15078,6 +15185,7 @@ var FECManager = class _FECManager {
|
|
|
15078
15185
|
dropSet(this.#cleanlyCompletedBlocks);
|
|
15079
15186
|
dropSet(this.#repairRequestedBlocks);
|
|
15080
15187
|
dropSet(this.#workerRecoveredBlocks);
|
|
15188
|
+
dropSet(this.#refusedRecoveries);
|
|
15081
15189
|
dropSet(this.#startupAlignedPackets);
|
|
15082
15190
|
dropSet(this.#startupPartialBlocks);
|
|
15083
15191
|
dropSet(this.#startupRepairReadyPackets);
|
|
@@ -15191,7 +15299,9 @@ var FECManager = class _FECManager {
|
|
|
15191
15299
|
moqSymbols: 0,
|
|
15192
15300
|
complete: false
|
|
15193
15301
|
};
|
|
15194
|
-
|
|
15302
|
+
const receivedAtMs = this.#receiptNow();
|
|
15303
|
+
const anchorMs = this.#blockAnchorsMs.get(blockKey);
|
|
15304
|
+
if (anchorMs === void 0 || receivedAtMs < anchorMs) this.#blockAnchorsMs.set(blockKey, receivedAtMs);
|
|
15195
15305
|
if (source === "multicast") counts.multicastSymbols++;
|
|
15196
15306
|
else counts.moqSymbols++;
|
|
15197
15307
|
this.#blockPathCounts.set(blockKey, counts);
|
|
@@ -15352,6 +15462,18 @@ var FECManager = class _FECManager {
|
|
|
15352
15462
|
blockAnchorAtMs(sbn, packetId, catalogTrackName) {
|
|
15353
15463
|
return this.#blockAnchorsMs.get(this.#blockKey(sbn, packetId, catalogTrackName));
|
|
15354
15464
|
}
|
|
15465
|
+
/**
|
|
15466
|
+
* How long ago (ms) the oldest open block of the stream, one with an FEC
|
|
15467
|
+
* deadline, took its first accepted symbol; undefined with none open.
|
|
15468
|
+
*/
|
|
15469
|
+
oldestOpenBlockAgeMs(packetId, catalogTrackName) {
|
|
15470
|
+
let oldest = Infinity;
|
|
15471
|
+
for (const key of this.#activeBlocks.keys()) {
|
|
15472
|
+
if (!this.#belongsToStream(key, packetId, catalogTrackName)) continue;
|
|
15473
|
+
oldest = Math.min(oldest, this.#blockAnchorsMs.get(key) ?? Infinity);
|
|
15474
|
+
}
|
|
15475
|
+
return oldest === Infinity ? void 0 : performance.now() - oldest;
|
|
15476
|
+
}
|
|
15355
15477
|
/** Absolute fail deadline of a still-open block, or undefined. */
|
|
15356
15478
|
blockDeadlineAtMs(sbn, packetId, catalogTrackName) {
|
|
15357
15479
|
return this.#activeBlockDeadlinesMs.get(this.#blockKey(sbn, packetId, catalogTrackName));
|
|
@@ -15852,7 +15974,25 @@ var FECManager = class _FECManager {
|
|
|
15852
15974
|
const blockKey = this.#blockKey(meta.sbn, packetId, recoveredCatalogTrackName);
|
|
15853
15975
|
const cleanlyCompleted = this.#cleanlyCompletedBlocks.has(blockKey);
|
|
15854
15976
|
if (!cleanlyCompleted) {
|
|
15855
|
-
|
|
15977
|
+
const symbolKey = `${blockKey}:${esi}`;
|
|
15978
|
+
if (!this.#seenSymbols.has(symbolKey)) {
|
|
15979
|
+
this.#seenSymbols.add(symbolKey);
|
|
15980
|
+
const admitted = this.onBlockRecovered?.(
|
|
15981
|
+
meta.sbn,
|
|
15982
|
+
packetId,
|
|
15983
|
+
data,
|
|
15984
|
+
meta,
|
|
15985
|
+
recoveredCatalogTrackName
|
|
15986
|
+
);
|
|
15987
|
+
if (admitted === false) {
|
|
15988
|
+
this.#refusedRecoveries.add(blockKey);
|
|
15989
|
+
if (this.#refusedRecoveries.size > 1e3) dropOldest(this.#refusedRecoveries, 500);
|
|
15990
|
+
}
|
|
15991
|
+
}
|
|
15992
|
+
}
|
|
15993
|
+
const refused = esi >= meta.ssbLength - 1 && this.#refusedRecoveries.delete(blockKey);
|
|
15994
|
+
if (esi >= meta.ssbLength - 1 && !refused && !this.#completedBlocks.has(blockKey)) {
|
|
15995
|
+
if (!cleanlyCompleted && !this.#workerRecoveredBlocks.has(blockKey)) {
|
|
15856
15996
|
this.#traceLifecycle(
|
|
15857
15997
|
"recovered",
|
|
15858
15998
|
meta.sbn,
|
|
@@ -15867,19 +16007,6 @@ var FECManager = class _FECManager {
|
|
|
15867
16007
|
}
|
|
15868
16008
|
this.#noteBlockRepaired(blockKey, meta.sbn, packetId, recoveredCatalogTrackName);
|
|
15869
16009
|
}
|
|
15870
|
-
const symbolKey = `${blockKey}:${esi}`;
|
|
15871
|
-
if (!this.#seenSymbols.has(symbolKey)) {
|
|
15872
|
-
this.#seenSymbols.add(symbolKey);
|
|
15873
|
-
this.onBlockRecovered?.(
|
|
15874
|
-
meta.sbn,
|
|
15875
|
-
packetId,
|
|
15876
|
-
data,
|
|
15877
|
-
meta,
|
|
15878
|
-
recoveredCatalogTrackName
|
|
15879
|
-
);
|
|
15880
|
-
}
|
|
15881
|
-
}
|
|
15882
|
-
if (esi >= meta.ssbLength - 1 && !this.#completedBlocks.has(blockKey)) {
|
|
15883
16010
|
this.cancelBlockTimeout(meta.sbn, packetId, recoveredCatalogTrackName);
|
|
15884
16011
|
if (recoveredCatalogTrackName === void 0) {
|
|
15885
16012
|
this.onBlockComplete?.(meta.sbn, packetId);
|
|
@@ -16174,66 +16301,26 @@ var FECManager = class _FECManager {
|
|
|
16174
16301
|
const config = this.#trackConfigs.get(packetId);
|
|
16175
16302
|
const layers = config?.repairLayers;
|
|
16176
16303
|
if (layers === void 0 || layers.length === 0) return true;
|
|
16177
|
-
|
|
16178
|
-
|
|
16304
|
+
let admission = this.#heldLayerStates.get(streamKey);
|
|
16305
|
+
if (admission === void 0) {
|
|
16306
|
+
admission = new HeldLayerAdmission(layers, config.heldLayers);
|
|
16307
|
+
this.#heldLayerStates.set(streamKey, admission);
|
|
16179
16308
|
}
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
this.#
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
|
|
16195
|
-
}
|
|
16196
|
-
} else {
|
|
16197
|
-
if (state.newest === void 0 || serial32Precedes(state.newest, ssStart)) state.newest = ssStart;
|
|
16198
|
-
const stale = (s) => serial32Distance(s, state.newest) > horizon;
|
|
16199
|
-
if (state.latestBelow !== void 0 && stale(state.latestBelow)) {
|
|
16200
|
-
delete state.latestBelow;
|
|
16201
|
-
delete state.latestBelowRsb;
|
|
16202
|
-
}
|
|
16203
|
-
if (state.anchor !== void 0 && stale(state.anchor)) state.settled = true;
|
|
16204
|
-
if (rsbLength < state.total) {
|
|
16205
|
-
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
16206
|
-
else if (stale(ssStart)) continue;
|
|
16207
|
-
else if (state.settled || state.anchor !== void 0 && !serial32Precedes(ssStart, state.anchor)) code = "below_anchored_total";
|
|
16208
|
-
else if (state.latestBelow === void 0 || serial32Precedes(state.latestBelow, ssStart)) {
|
|
16209
|
-
state.latestBelow = ssStart;
|
|
16210
|
-
state.latestBelowRsb = rsbLength;
|
|
16211
|
-
}
|
|
16212
|
-
} else if (!state.settled && !stale(ssStart)) {
|
|
16213
|
-
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
16214
|
-
if (state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
16215
|
-
code = "below_anchored_total";
|
|
16216
|
-
ssStart = state.latestBelow;
|
|
16217
|
-
rsbLength = state.latestBelowRsb;
|
|
16218
|
-
}
|
|
16219
|
-
}
|
|
16220
|
-
}
|
|
16221
|
-
if (code !== void 0) {
|
|
16222
|
-
const reason = {
|
|
16223
|
-
code,
|
|
16224
|
-
layer: state.layer,
|
|
16225
|
-
total: state.total,
|
|
16226
|
-
rsbLength,
|
|
16227
|
-
ssStart,
|
|
16228
|
-
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
16229
|
-
};
|
|
16230
|
-
this.#fecFailClosed.set(streamKey, reason);
|
|
16231
|
-
this.#onFecFailClosed?.({ ...reason, packetId, catalogTrackName });
|
|
16232
|
-
console.warn(
|
|
16233
|
-
`[FECManager] FEC failed closed (fec-01 6.3.9) packetId=${packetId} track=${catalogTrackName ?? "-"}: ${JSON.stringify(reason)}`
|
|
16234
|
-
);
|
|
16235
|
-
return false;
|
|
16236
|
-
}
|
|
16309
|
+
const verdict = admission.admit(
|
|
16310
|
+
repairTrackName,
|
|
16311
|
+
ssStart,
|
|
16312
|
+
rsbLength,
|
|
16313
|
+
this.#layerAnchorHorizon(packetId),
|
|
16314
|
+
this.#sourceFrontiers.get(streamKey)?.sbn
|
|
16315
|
+
);
|
|
16316
|
+
if (verdict === "discard") return false;
|
|
16317
|
+
if (verdict !== void 0) {
|
|
16318
|
+
this.#fecFailClosed.set(streamKey, verdict);
|
|
16319
|
+
this.#onFecFailClosed?.({ ...verdict, packetId, catalogTrackName });
|
|
16320
|
+
console.warn(
|
|
16321
|
+
`[FECManager] FEC failed closed (fec-01 6.3.9) packetId=${packetId} track=${catalogTrackName ?? "-"}: ${JSON.stringify(verdict)}`
|
|
16322
|
+
);
|
|
16323
|
+
return false;
|
|
16237
16324
|
}
|
|
16238
16325
|
return true;
|
|
16239
16326
|
}
|
|
@@ -16243,15 +16330,13 @@ var FECManager = class _FECManager {
|
|
|
16243
16330
|
* times K, capped at 2^30 so compared serials stay inside one half-window.
|
|
16244
16331
|
*/
|
|
16245
16332
|
#layerAnchorHorizon(packetId) {
|
|
16246
|
-
const cap = 1073741824;
|
|
16247
16333
|
let blocks;
|
|
16248
16334
|
try {
|
|
16249
16335
|
blocks = this.isoRetentionBlocksForPacket(packetId);
|
|
16250
16336
|
} catch {
|
|
16251
16337
|
blocks = void 0;
|
|
16252
16338
|
}
|
|
16253
|
-
|
|
16254
|
-
return blocks === void 0 || k <= 0 ? cap : Math.min(cap, blocks * k);
|
|
16339
|
+
return heldLayerAnchorHorizon(blocks, this.#trackConfigs.get(packetId)?.k ?? 0);
|
|
16255
16340
|
}
|
|
16256
16341
|
/**
|
|
16257
16342
|
* Report each stream whose FEC fails closed at runtime (fec-01 6.3.9), once,
|
|
@@ -16491,6 +16576,33 @@ var FECManager = class _FECManager {
|
|
|
16491
16576
|
}
|
|
16492
16577
|
queueMicrotask(() => this.#syncDecoderBlockOwners.delete(key));
|
|
16493
16578
|
}
|
|
16579
|
+
/**
|
|
16580
|
+
* Run `fn` with `receivedAtMs` (performance.now() clock) as the receipt
|
|
16581
|
+
* time of what it feeds. Within a consumer the bc-provenance profile
|
|
16582
|
+
* governs, a datagram held on a miss and admitted later keeps its receipt
|
|
16583
|
+
* time (fec-01 9): it anchors its block's ts and arms the block's
|
|
16584
|
+
* deadlines from then, never later than they are already armed.
|
|
16585
|
+
*/
|
|
16586
|
+
withReceiptTime(receivedAtMs, fn) {
|
|
16587
|
+
const outer = this.#receiptAtMs;
|
|
16588
|
+
this.#receiptAtMs = receivedAtMs;
|
|
16589
|
+
try {
|
|
16590
|
+
fn();
|
|
16591
|
+
} finally {
|
|
16592
|
+
this.#receiptAtMs = outer;
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16595
|
+
#receiptNow() {
|
|
16596
|
+
return this.#receiptAtMs ?? performance.now();
|
|
16597
|
+
}
|
|
16598
|
+
/**
|
|
16599
|
+
* A held packet admitted late arms from its (earlier) receipt time; that
|
|
16600
|
+
* never pulls in a deadline later activity already armed. Outside a
|
|
16601
|
+
* receipt scope a re-arm replaces the deadline, as a narrower bound may.
|
|
16602
|
+
*/
|
|
16603
|
+
#notEarlierUnderReceipt(deadlineMs, armedMs) {
|
|
16604
|
+
return this.#receiptAtMs === void 0 || armedMs === void 0 ? deadlineMs : Math.max(deadlineMs, armedMs);
|
|
16605
|
+
}
|
|
16494
16606
|
/** Refresh deadlines after accepted block activity. */
|
|
16495
16607
|
noteBlockActivity(sbn, packetId, catalogTrackName, sourceProgress = false) {
|
|
16496
16608
|
if (!this.#trackConfigs.has(packetId)) return;
|
|
@@ -16669,6 +16781,7 @@ var FECManager = class _FECManager {
|
|
|
16669
16781
|
this.#cleanlyCompletedBlocks.clear();
|
|
16670
16782
|
this.#repairRequestedBlocks.clear();
|
|
16671
16783
|
this.#workerRecoveredBlocks.clear();
|
|
16784
|
+
this.#refusedRecoveries.clear();
|
|
16672
16785
|
this.#syncDecoderBlockOwners.clear();
|
|
16673
16786
|
this.#startupAlignedPackets.clear();
|
|
16674
16787
|
this.#startupPartialBlocks.clear();
|
|
@@ -19366,11 +19479,22 @@ var MmtpRouter = class {
|
|
|
19366
19479
|
#fecManager;
|
|
19367
19480
|
#defaultFecMapper;
|
|
19368
19481
|
#fecMappersByPacketId = /* @__PURE__ */ new Map();
|
|
19369
|
-
|
|
19370
|
-
#
|
|
19371
|
-
#
|
|
19372
|
-
#
|
|
19373
|
-
#
|
|
19482
|
+
/** Protected flows: one 7.2.7 receiver each, and the ingress its datagrams arrive with. */
|
|
19483
|
+
#provenance = /* @__PURE__ */ new Map();
|
|
19484
|
+
#onFlowProtected = null;
|
|
19485
|
+
#unadmittedListedDrops = 0;
|
|
19486
|
+
#unlistedRepairSetAside = 0;
|
|
19487
|
+
/** The ingress a held datagram arrived with; the receiver returns the same bytes on release. */
|
|
19488
|
+
#heldIngress = /* @__PURE__ */ new WeakMap();
|
|
19489
|
+
#unicastRepair = false;
|
|
19490
|
+
/** How to route a held unicast (MoQ) datagram once a record admits it. */
|
|
19491
|
+
#heldReplay = /* @__PURE__ */ new WeakMap();
|
|
19492
|
+
/** When a held datagram was received: admitted later, it keeps that receipt time (fec-01 9). */
|
|
19493
|
+
#heldAt = /* @__PURE__ */ new WeakMap();
|
|
19494
|
+
/** The flow whose receiver admitted a stream's source datagrams, by packetId then catalog track name: recovered packets answer to it. */
|
|
19495
|
+
#provenanceStreamFlow = /* @__PURE__ */ new Map();
|
|
19496
|
+
#unregisteredFlowDrops = 0;
|
|
19497
|
+
#ambiguousRecoveredDrops = 0;
|
|
19374
19498
|
// Temp state set by #processMMTP before calling parser.feed(),
|
|
19375
19499
|
// read by the parser's onFrame callback for CMAF assembly + dedup.
|
|
19376
19500
|
#currentFecInfo;
|
|
@@ -19507,10 +19631,6 @@ var MmtpRouter = class {
|
|
|
19507
19631
|
this.#fecManager = config?.fecManager ?? null;
|
|
19508
19632
|
this.#onFrameGap = config?.onFrameGap ?? null;
|
|
19509
19633
|
this.#defaultFecMapper = config?.fecMapper ?? null;
|
|
19510
|
-
this.#manifestVerifier = config?.manifestVerifier ?? null;
|
|
19511
|
-
this.#ownsManifestVerifier = config?.manifestVerifierOwnership !== "caller";
|
|
19512
|
-
this.#resolveManifestIdentity = config?.resolveManifestIdentity ?? null;
|
|
19513
|
-
this.#resolveManifestIdentityForRoute = config?.resolveManifestIdentityForRoute ?? null;
|
|
19514
19634
|
this.#parser = new MmtpContainerParser({
|
|
19515
19635
|
mfuTimeoutMs: config?.mfuTimeoutMs,
|
|
19516
19636
|
reassemblyPolicy: config?.reassemblyPolicy,
|
|
@@ -20042,29 +20162,189 @@ var MmtpRouter = class {
|
|
|
20042
20162
|
}
|
|
20043
20163
|
});
|
|
20044
20164
|
}
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
this.#
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20165
|
+
/**
|
|
20166
|
+
* Protect a flow (draft-ramadan-moq-multicast-01 7.2.7): every multicast
|
|
20167
|
+
* datagram whose ingress names `ingress.flow`, source and repair alike, is
|
|
20168
|
+
* admitted by `receiver` before anything reads it, and an unadmitted one is
|
|
20169
|
+
* held or dropped. The caller owns the receiver's configuration; the router
|
|
20170
|
+
* releases it on `releaseProvenanceFlow` and on dispose.
|
|
20171
|
+
*/
|
|
20172
|
+
registerProvenanceFlow(ingress, receiver) {
|
|
20173
|
+
this.#provenance.get(ingress.flow)?.receiver.release();
|
|
20174
|
+
this.#provenance.set(ingress.flow, { receiver, ingress });
|
|
20175
|
+
receiver.keepPendingAgeMs = () => this.#oldestOpenBlockAgeMs(ingress.flow);
|
|
20176
|
+
}
|
|
20177
|
+
/**
|
|
20178
|
+
* Declare that this receiver can request unicast repair (a repair FETCH
|
|
20179
|
+
* scheduler is wired): a protected flow then keeps the pending entries of
|
|
20180
|
+
* its open blocks for the repair to consume (7.2.7 step 2).
|
|
20181
|
+
*/
|
|
20182
|
+
setUnicastRepair(enabled) {
|
|
20183
|
+
this.#unicastRepair = enabled;
|
|
20184
|
+
}
|
|
20185
|
+
#oldestOpenBlockAgeMs(flow) {
|
|
20186
|
+
const entry = this.#provenance.get(flow);
|
|
20187
|
+
if (!this.#unicastRepair || !entry) return void 0;
|
|
20188
|
+
let age;
|
|
20189
|
+
for (const { name, packetId } of entry.ingress.tracks) {
|
|
20190
|
+
const a = name ? this.#fecManager?.oldestOpenBlockAgeMs?.(packetId, name) : void 0;
|
|
20191
|
+
if (a !== void 0 && (age === void 0 || a > age)) age = a;
|
|
20192
|
+
}
|
|
20193
|
+
return age;
|
|
20194
|
+
}
|
|
20195
|
+
/**
|
|
20196
|
+
* Replace a registered flow's ingress (a rendition switch staged, committed
|
|
20197
|
+
* or cancelled) while keeping its receiver; a no-op for an unregistered flow.
|
|
20198
|
+
*/
|
|
20199
|
+
updateProvenanceFlowIngress(ingress) {
|
|
20200
|
+
const entry = this.#provenance.get(ingress.flow);
|
|
20201
|
+
if (!entry) return;
|
|
20202
|
+
this.#provenance.set(ingress.flow, { ...entry, ingress });
|
|
20203
|
+
if (entry.protected) {
|
|
20204
|
+
for (const { name, packetId } of ingress.tracks) {
|
|
20205
|
+
if (name && !entry.ingress.tracks.some((t) => t.name === name && t.packetId === packetId)) {
|
|
20206
|
+
this.#fecManager?.resetTrack?.(packetId, name);
|
|
20063
20207
|
}
|
|
20064
20208
|
}
|
|
20065
|
-
this.#manifestRecoveryQueues.clear();
|
|
20066
20209
|
}
|
|
20067
20210
|
}
|
|
20211
|
+
/**
|
|
20212
|
+
* Called once per protection of a flow: at the admission that makes this
|
|
20213
|
+
* router's consumers protected under it (7.2.7), after their unadmitted
|
|
20214
|
+
* holdings of its listed tracks are discarded. Again after an unprotect.
|
|
20215
|
+
*/
|
|
20216
|
+
onProvenanceFlowProtected(cb) {
|
|
20217
|
+
this.#onFlowProtected = cb;
|
|
20218
|
+
}
|
|
20219
|
+
/**
|
|
20220
|
+
* The receiver ceased to hold the flow's endpoint on failover (7.2.7): drop
|
|
20221
|
+
* its state and hold queues (no expiry counted) and un-protect its tracks,
|
|
20222
|
+
* which then take MoQ again. The registration stays, so multicast resuming
|
|
20223
|
+
* protects the flow anew at its next admission.
|
|
20224
|
+
*/
|
|
20225
|
+
unprotectProvenanceFlow(flow) {
|
|
20226
|
+
const entry = this.#provenance.get(flow);
|
|
20227
|
+
if (!entry) return;
|
|
20228
|
+
entry.receiver.release();
|
|
20229
|
+
entry.protected = false;
|
|
20230
|
+
}
|
|
20231
|
+
/** MoQ packets of a track a protected flow lists, dropped on its unicast path: duplicate, overflow, or listed by more than one flow. */
|
|
20232
|
+
get unadmittedListedDropCount() {
|
|
20233
|
+
return this.#unadmittedListedDrops;
|
|
20234
|
+
}
|
|
20235
|
+
/** MoQ repair symbols of an unlisted track kept out of a governed FEC instance (7.2.7 step 2). */
|
|
20236
|
+
get unlistedRepairSetAsideCount() {
|
|
20237
|
+
return this.#unlistedRepairSetAside;
|
|
20238
|
+
}
|
|
20239
|
+
/**
|
|
20240
|
+
* A MoQ repair symbol no protected flow lists, owned by a source track one
|
|
20241
|
+
* does: its FEC instance is governed (7.2.7 step 2) and takes only admitted
|
|
20242
|
+
* symbols, so the symbol is set aside. Un-listing needs no hook: an
|
|
20243
|
+
* unprotected or released flow lists nothing.
|
|
20244
|
+
* ponytail: set aside = dropped. 7.2.7 lets it enter a decode (MAY), only
|
|
20245
|
+
* for a shortfall; the unlisted-symbol pool (BLO-49773 item 2) adds that.
|
|
20246
|
+
*/
|
|
20247
|
+
#setAsideUnlistedRepair(ownerPacketId) {
|
|
20248
|
+
for (const entry of this.#provenance.values()) {
|
|
20249
|
+
if (entry.protected && ingressHasPacketId(entry.ingress, ownerPacketId)) {
|
|
20250
|
+
this.#unlistedRepairSetAside++;
|
|
20251
|
+
return true;
|
|
20252
|
+
}
|
|
20253
|
+
}
|
|
20254
|
+
return false;
|
|
20255
|
+
}
|
|
20256
|
+
/**
|
|
20257
|
+
* The admission that makes this router protected under `flow` (7.2.7):
|
|
20258
|
+
* its FEC instances discard what they hold of the flow's listed tracks so
|
|
20259
|
+
* no unadmitted symbol stands for an admitted one, then notify. Before it
|
|
20260
|
+
* nothing of the flow was admitted, so every such symbol is unadmitted.
|
|
20261
|
+
* The playback parse ledger is kept: it records MoQ copies already played,
|
|
20262
|
+
* not holdings, and clearing it re-emits that media (audio twice).
|
|
20263
|
+
* The FEC reset is the whole 7.2.7 discard here: no keyframe-overlay
|
|
20264
|
+
* instance exists to hold anything (repair-layers.ts is the catalog
|
|
20265
|
+
* contract only, no decoder), and packet_id 0 is never a listed track
|
|
20266
|
+
* (`ingressHasPacketId`), so nothing of it is held for the flow.
|
|
20267
|
+
*/
|
|
20268
|
+
#noteFlowAdmitted(flow) {
|
|
20269
|
+
const entry = this.#provenance.get(flow);
|
|
20270
|
+
if (!entry || entry.protected) return;
|
|
20271
|
+
entry.protected = true;
|
|
20272
|
+
for (const { name, packetId } of entry.ingress.tracks) {
|
|
20273
|
+
if (name) this.#fecManager?.resetTrack?.(packetId, name);
|
|
20274
|
+
}
|
|
20275
|
+
this.#onFlowProtected?.(flow);
|
|
20276
|
+
}
|
|
20277
|
+
/**
|
|
20278
|
+
* Admit a MoQ datagram of `packetId` on the unicast path of the protected
|
|
20279
|
+
* flow that lists it (7.2.7). `unlisted`: no protected flow lists it, so it
|
|
20280
|
+
* is read as before. A held datagram is routed by `replay` when a record
|
|
20281
|
+
* admits it; one more than one protected flow lists is dropped. So is an
|
|
20282
|
+
* overlap copy: it never enters FEC accounting, so admitting it would leave
|
|
20283
|
+
* its multicast twin a duplicate and the block one symbol short.
|
|
20284
|
+
*/
|
|
20285
|
+
#admitUnicast(packetId, packet, replay, overlapCopy = false) {
|
|
20286
|
+
let receiver;
|
|
20287
|
+
for (const entry of this.#provenance.values()) {
|
|
20288
|
+
if (!entry.protected || !ingressHasPacketId(entry.ingress, packetId)) continue;
|
|
20289
|
+
if (receiver || overlapCopy) {
|
|
20290
|
+
this.#unadmittedListedDrops++;
|
|
20291
|
+
return "dropped";
|
|
20292
|
+
}
|
|
20293
|
+
receiver = entry.receiver;
|
|
20294
|
+
}
|
|
20295
|
+
if (!receiver) return "unlisted";
|
|
20296
|
+
const { verdict } = receiver.onDatagram(packet, "unicast");
|
|
20297
|
+
if (verdict === "admitted") return "admitted";
|
|
20298
|
+
if (verdict === "held") {
|
|
20299
|
+
this.#heldReplay.set(packet, replay);
|
|
20300
|
+
this.#heldAt.set(packet, performance.now());
|
|
20301
|
+
return "held";
|
|
20302
|
+
}
|
|
20303
|
+
this.#unadmittedListedDrops++;
|
|
20304
|
+
return "dropped";
|
|
20305
|
+
}
|
|
20306
|
+
/**
|
|
20307
|
+
* Feed one bc-provenance record of `flow`: a datagram from `manifestPort`
|
|
20308
|
+
* (`multicast`) or an object of the manifest track (`mirror`). Held
|
|
20309
|
+
* datagrams the record admits are routed before this returns.
|
|
20310
|
+
*/
|
|
20311
|
+
feedProvenanceRecord(flow, record, path) {
|
|
20312
|
+
const entry = this.#provenance.get(flow);
|
|
20313
|
+
if (!entry || this.#disposed) return "unregistered";
|
|
20314
|
+
const result = entry.receiver.onRecord(record, path);
|
|
20315
|
+
if (result.admitted.length > 0) {
|
|
20316
|
+
this.#noteFlowAdmitted(flow);
|
|
20317
|
+
this.#withDeferredFecPokes(() => {
|
|
20318
|
+
for (const datagram of result.admitted) {
|
|
20319
|
+
const route = () => {
|
|
20320
|
+
const replay = this.#heldReplay.get(datagram);
|
|
20321
|
+
if (replay) replay();
|
|
20322
|
+
else this.#processMMTP(datagram, "multicast", true, this.#heldIngress.get(datagram) ?? entry.ingress, false, true);
|
|
20323
|
+
};
|
|
20324
|
+
const heldAt = this.#heldAt.get(datagram);
|
|
20325
|
+
if (heldAt !== void 0 && this.#fecManager?.withReceiptTime) this.#fecManager.withReceiptTime(heldAt, route);
|
|
20326
|
+
else route();
|
|
20327
|
+
}
|
|
20328
|
+
});
|
|
20329
|
+
}
|
|
20330
|
+
return result.verdict;
|
|
20331
|
+
}
|
|
20332
|
+
/** The receiver ceased to hold the flow: its hold queues are dropped, not expired. */
|
|
20333
|
+
releaseProvenanceFlow(flow) {
|
|
20334
|
+
this.#provenance.get(flow)?.receiver.release();
|
|
20335
|
+
this.#provenance.delete(flow);
|
|
20336
|
+
for (const byName of this.#provenanceStreamFlow.values()) {
|
|
20337
|
+
for (const [name, bound] of byName) if (bound === flow) byName.delete(name);
|
|
20338
|
+
}
|
|
20339
|
+
}
|
|
20340
|
+
/** Multicast datagrams dropped because their ingress names a flow with no registered receiver. */
|
|
20341
|
+
get unregisteredFlowDropCount() {
|
|
20342
|
+
return this.#unregisteredFlowDrops;
|
|
20343
|
+
}
|
|
20344
|
+
/** Recovered packets dropped because more than one protected flow could own their stream. */
|
|
20345
|
+
get ambiguousRecoveredDropCount() {
|
|
20346
|
+
return this.#ambiguousRecoveredDrops;
|
|
20347
|
+
}
|
|
20068
20348
|
/**
|
|
20069
20349
|
* Main entry point: feed a raw MMTP packet for processing.
|
|
20070
20350
|
*
|
|
@@ -20079,10 +20359,10 @@ var MmtpRouter = class {
|
|
|
20079
20359
|
* they may never have crossed — and that attribution is what the physical-
|
|
20080
20360
|
* delivery evidence path is built on. Absence is not multicast; say which.
|
|
20081
20361
|
*/
|
|
20082
|
-
feedPacket(packet, source,
|
|
20362
|
+
feedPacket(packet, source, ingress) {
|
|
20083
20363
|
if (this.#disposed) return;
|
|
20084
20364
|
this.#withDeferredFecPokes(() => {
|
|
20085
|
-
this.#processMMTP(packet, source,
|
|
20365
|
+
this.#processMMTP(packet, source, false, ingress);
|
|
20086
20366
|
});
|
|
20087
20367
|
}
|
|
20088
20368
|
/**
|
|
@@ -20117,7 +20397,7 @@ var MmtpRouter = class {
|
|
|
20117
20397
|
feedOverlapCopy(packet, ingress) {
|
|
20118
20398
|
if (this.#disposed) return;
|
|
20119
20399
|
this.#withDeferredFecPokes(() => {
|
|
20120
|
-
this.#processMMTP(packet, "moq",
|
|
20400
|
+
this.#processMMTP(packet, "moq", false, ingress, true);
|
|
20121
20401
|
});
|
|
20122
20402
|
}
|
|
20123
20403
|
/**
|
|
@@ -20130,7 +20410,7 @@ var MmtpRouter = class {
|
|
|
20130
20410
|
this.#feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName, repairTrackName);
|
|
20131
20411
|
});
|
|
20132
20412
|
}
|
|
20133
|
-
#feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName, repairTrackName) {
|
|
20413
|
+
#feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName, repairTrackName, admitted = false) {
|
|
20134
20414
|
let header;
|
|
20135
20415
|
try {
|
|
20136
20416
|
header = parseMMTPHeader(packet);
|
|
@@ -20147,6 +20427,11 @@ var MmtpRouter = class {
|
|
|
20147
20427
|
`[MmtpRouter] Dropping MoQ repair packet pid=${repairPacketId}: owner hint is required`
|
|
20148
20428
|
);
|
|
20149
20429
|
}
|
|
20430
|
+
if (ownerPacketId != null && !admitted) {
|
|
20431
|
+
const admit = this.#admitUnicast(repairPacketId, packet, () => this.#feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName, repairTrackName, true));
|
|
20432
|
+
if (admit === "held" || admit === "dropped") return;
|
|
20433
|
+
if (admit === "unlisted" && this.#setAsideUnlistedRepair(ownerPacketId)) return;
|
|
20434
|
+
}
|
|
20150
20435
|
if (ownerPacketId != null && this.#fecManager) {
|
|
20151
20436
|
this.#moqRepairPidCounts[repairPacketId] = (this.#moqRepairPidCounts[repairPacketId] || 0) + 1;
|
|
20152
20437
|
this.#moqRepairOwnerCounts[ownerPacketId] = (this.#moqRepairOwnerCounts[ownerPacketId] || 0) + 1;
|
|
@@ -20290,10 +20575,11 @@ var MmtpRouter = class {
|
|
|
20290
20575
|
catalogTrackName
|
|
20291
20576
|
);
|
|
20292
20577
|
if (recovered && !alreadyCompleted) {
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
|
|
20578
|
+
this.#routeRecoveredBlock(blockStart, recovered, ownerPacketId, catalogTrackName, () => {
|
|
20579
|
+
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
20580
|
+
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
20581
|
+
fec.markBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20582
|
+
});
|
|
20297
20583
|
}
|
|
20298
20584
|
}
|
|
20299
20585
|
/**
|
|
@@ -20334,10 +20620,11 @@ var MmtpRouter = class {
|
|
|
20334
20620
|
const alreadyCompleted = fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20335
20621
|
const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData, catalogTrackName);
|
|
20336
20622
|
if (!recovered || alreadyCompleted) continue;
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20623
|
+
this.#routeRecoveredBlock(blockStart, recovered, ownerPacketId, catalogTrackName, () => {
|
|
20624
|
+
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
20625
|
+
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
20626
|
+
fec.markBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20627
|
+
});
|
|
20341
20628
|
}
|
|
20342
20629
|
}
|
|
20343
20630
|
#resolveRepairOwnerPacketId(packetId, ingress) {
|
|
@@ -20360,14 +20647,18 @@ var MmtpRouter = class {
|
|
|
20360
20647
|
* Sync WASM decoders return the block's K' = SSB_length fixed-width
|
|
20361
20648
|
* ssbg_mode1 source symbols concatenated. */
|
|
20362
20649
|
routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
|
|
20363
|
-
|
|
20650
|
+
let recovered = false;
|
|
20651
|
+
this.#withDeferredFecPokes(() => {
|
|
20652
|
+
recovered = this.#routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
|
|
20653
|
+
});
|
|
20654
|
+
return recovered;
|
|
20364
20655
|
}
|
|
20365
20656
|
/** Route a single recovered source symbol through the parser + dedup pipeline.
|
|
20366
20657
|
* Worker-backed decoders emit one padded source symbol per frame event. */
|
|
20367
20658
|
routeRecoveredSymbol(blockId, data, packetId, esi = 0, catalogTrackName) {
|
|
20368
20659
|
const blockProfile = this.#blockProfileFor(packetId);
|
|
20369
20660
|
const result = processFecSymbol(data, { ownerPacketId: packetId, index: esi, blockProfile });
|
|
20370
|
-
this.#routeVerifiedFecSymbols(blockId, result, packetId, catalogTrackName);
|
|
20661
|
+
return this.#routeVerifiedFecSymbols(blockId, result, packetId, catalogTrackName);
|
|
20371
20662
|
}
|
|
20372
20663
|
// ── Keyframe timer cancellation ─────────────────────────────────
|
|
20373
20664
|
/** Cancel the active keyframe loss timer.
|
|
@@ -20410,7 +20701,6 @@ var MmtpRouter = class {
|
|
|
20410
20701
|
this.#lastDirectSourceAudioCursorByStream.delete(streamKey);
|
|
20411
20702
|
this.#lastDirectEmittedAudioCursorByStream.delete(streamKey);
|
|
20412
20703
|
this.#pendingDirectRecoveredAudioByStream.delete(streamKey);
|
|
20413
|
-
this.#manifestRecoveryQueues.delete(streamKey);
|
|
20414
20704
|
this.#deliveredVideoHighWaterByStream.delete(streamKey);
|
|
20415
20705
|
this.#stream(packetId, catalogTrackName).duplicates.clear();
|
|
20416
20706
|
const assemblersToReset = /* @__PURE__ */ new Set();
|
|
@@ -20460,7 +20750,6 @@ var MmtpRouter = class {
|
|
|
20460
20750
|
this.#lastDirectSourceAudioCursorByStream.delete(key);
|
|
20461
20751
|
this.#lastDirectEmittedAudioCursorByStream.delete(key);
|
|
20462
20752
|
this.#pendingDirectRecoveredAudioByStream.delete(key);
|
|
20463
|
-
this.#manifestRecoveryQueues.delete(key);
|
|
20464
20753
|
this.#trackInitDelivered.delete(key);
|
|
20465
20754
|
this.#parserDiagnostics.delete(key);
|
|
20466
20755
|
this.#pendingFecPokes.delete(key);
|
|
@@ -20721,8 +21010,9 @@ var MmtpRouter = class {
|
|
|
20721
21010
|
this.#parserDiagnostics.clear();
|
|
20722
21011
|
this.#catalogTrackNamesByPacketId.clear();
|
|
20723
21012
|
this.#repairOwnersByPacketId.clear();
|
|
20724
|
-
|
|
20725
|
-
this.#
|
|
21013
|
+
for (const { receiver } of this.#provenance.values()) receiver.release();
|
|
21014
|
+
this.#provenance.clear();
|
|
21015
|
+
this.#provenanceStreamFlow.clear();
|
|
20726
21016
|
for (const assembler of this.#mseAssemblers.values()) assembler.dispose();
|
|
20727
21017
|
this.#mseAssemblers.clear();
|
|
20728
21018
|
}
|
|
@@ -20999,7 +21289,7 @@ var MmtpRouter = class {
|
|
|
20999
21289
|
* 1. parseHeader() → extract packetId + fecPayloadId for FEC feeding
|
|
21000
21290
|
* 2. feed() → MFU extraction, reassembly, keyframe detection, emit via callbacks
|
|
21001
21291
|
*/
|
|
21002
|
-
#processMMTP(packet, source,
|
|
21292
|
+
#processMMTP(packet, source, manifestAdmitted = false, ingress, overlapCopy = false, releasedFromQuarantine = false) {
|
|
21003
21293
|
const parsed = this.#parser.parsePacket(packet);
|
|
21004
21294
|
if (!parsed) return;
|
|
21005
21295
|
const info = parsed.info;
|
|
@@ -21024,43 +21314,32 @@ var MmtpRouter = class {
|
|
|
21024
21314
|
}
|
|
21025
21315
|
const fecUnreadable = !info.isRepair && info.fecPayloadId != null && this.#fecManager !== null && this.#classifyPacketId(info.packetId) !== null && this.#configuredBlockProfile(info.packetId) === void 0;
|
|
21026
21316
|
if (fecUnreadable && !manifestAdmitted) this.#noteUnreadableFecPayloadId(info.packetId, "source");
|
|
21027
|
-
if (!info.isRepair &&
|
|
21028
|
-
let
|
|
21029
|
-
if (!
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
|
|
21035
|
-
|
|
21036
|
-
|
|
21037
|
-
recovered: false,
|
|
21038
|
-
sourceSymbolFormat: "full-mmtp-packet"
|
|
21039
|
-
}) : this.#resolveManifestIdentity(
|
|
21040
|
-
info.packetId,
|
|
21041
|
-
coordinates.sbn,
|
|
21042
|
-
coordinates.esi,
|
|
21043
|
-
catalogTrackName
|
|
21044
|
-
)) ?? void 0;
|
|
21045
|
-
}
|
|
21046
|
-
if (identity) {
|
|
21047
|
-
identity = {
|
|
21048
|
-
...identity,
|
|
21049
|
-
packetId: info.packetId,
|
|
21050
|
-
...info.fecPayloadId ? { ssId: info.fecPayloadId.ssId } : {}
|
|
21051
|
-
};
|
|
21052
|
-
const authenticated = info.fecPayloadId ? packet.subarray(0, packet.length - 4) : packet;
|
|
21053
|
-
let quarantined = false;
|
|
21054
|
-
const admission = this.#manifestVerifier.admitObject(identity, authenticated, () => {
|
|
21055
|
-
if (!this.#disposed) {
|
|
21056
|
-
this.#processMMTP(packet, source, identity, true, ingress, overlapCopy, quarantined);
|
|
21057
|
-
}
|
|
21058
|
-
});
|
|
21059
|
-
quarantined = admission.action === "stall";
|
|
21317
|
+
if (source === "multicast" && manifestAdmitted && !info.isRepair && ingress?.flow !== void 0) {
|
|
21318
|
+
let byName = this.#provenanceStreamFlow.get(info.packetId);
|
|
21319
|
+
if (!byName) this.#provenanceStreamFlow.set(info.packetId, byName = /* @__PURE__ */ new Map());
|
|
21320
|
+
byName.set(catalogTrackNameForPacket(ingress, info.packetId), ingress.flow);
|
|
21321
|
+
}
|
|
21322
|
+
if (source === "multicast" && !manifestAdmitted && ingress?.flow !== void 0) {
|
|
21323
|
+
const protectedFlow = this.#provenance.get(ingress.flow);
|
|
21324
|
+
if (!protectedFlow) {
|
|
21325
|
+
this.#unregisteredFlowDrops++;
|
|
21326
|
+
debugRouter(`[MmtpRouter] Dropping multicast packet pid=${info.packetId}: flow ${ingress.flow} has no receiver`);
|
|
21060
21327
|
return;
|
|
21061
21328
|
}
|
|
21062
|
-
|
|
21063
|
-
|
|
21329
|
+
const { verdict } = protectedFlow.receiver.onDatagram(packet, "multicast");
|
|
21330
|
+
if (verdict === "admitted") {
|
|
21331
|
+
this.#noteFlowAdmitted(ingress.flow);
|
|
21332
|
+
this.#processMMTP(packet, source, true, ingress, overlapCopy);
|
|
21333
|
+
} else if (verdict === "held") {
|
|
21334
|
+
this.#heldIngress.set(packet, ingress);
|
|
21335
|
+
this.#heldAt.set(packet, performance.now());
|
|
21336
|
+
}
|
|
21337
|
+
return;
|
|
21338
|
+
}
|
|
21339
|
+
if (source === "moq" && !manifestAdmitted) {
|
|
21340
|
+
const admit = this.#admitUnicast(info.packetId, packet, () => this.#processMMTP(packet, source, true, ingress, overlapCopy, true), overlapCopy);
|
|
21341
|
+
if (admit !== "unlisted") {
|
|
21342
|
+
if (admit === "admitted") this.#processMMTP(packet, source, true, ingress, overlapCopy);
|
|
21064
21343
|
return;
|
|
21065
21344
|
}
|
|
21066
21345
|
}
|
|
@@ -21070,6 +21349,7 @@ var MmtpRouter = class {
|
|
|
21070
21349
|
const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
|
|
21071
21350
|
if (ownerPacketId != null && this.#fecManager) {
|
|
21072
21351
|
if (hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
|
|
21352
|
+
if (source === "moq" && !manifestAdmitted && this.#setAsideUnlistedRepair(ownerPacketId)) return;
|
|
21073
21353
|
this.#repairPidCounts[info.packetId] = (this.#repairPidCounts[info.packetId] || 0) + 1;
|
|
21074
21354
|
const count = this.#repairPidCounts[info.packetId];
|
|
21075
21355
|
if (count <= 2 || count % 500 === 0) {
|
|
@@ -21237,36 +21517,22 @@ var MmtpRouter = class {
|
|
|
21237
21517
|
sourceFec.symbolData,
|
|
21238
21518
|
catalogTrackName
|
|
21239
21519
|
);
|
|
21240
|
-
if (recovered) {
|
|
21241
|
-
|
|
21242
|
-
|
|
21243
|
-
|
|
21244
|
-
|
|
21245
|
-
|
|
21246
|
-
sourceFec.sbn,
|
|
21247
|
-
info.packetId,
|
|
21248
|
-
catalogTrackName
|
|
21249
|
-
);
|
|
21250
|
-
}
|
|
21251
|
-
}
|
|
21252
|
-
if (!alreadyCompleted) {
|
|
21253
|
-
this.#fecManager.countRecovery(info.packetId, sourceFec.sbn, catalogTrackName);
|
|
21520
|
+
if (recovered && !alreadyCompleted) {
|
|
21521
|
+
const fec = this.#fecManager;
|
|
21522
|
+
const sbn = sourceFec.sbn;
|
|
21523
|
+
const packetId = info.packetId;
|
|
21524
|
+
const tracked = this.#classifyPacketId(packetId) !== null;
|
|
21525
|
+
this.#routeRecoveredBlock(sbn, recovered, packetId, catalogTrackName, () => {
|
|
21254
21526
|
if (catalogTrackName === void 0) {
|
|
21255
|
-
|
|
21527
|
+
if (tracked) fec.cancelBlockTimeout(sbn, packetId);
|
|
21528
|
+
fec.countRecovery(packetId, sbn, catalogTrackName);
|
|
21529
|
+
fec.markBlockCompleted(sbn, packetId);
|
|
21256
21530
|
} else {
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21260
|
-
catalogTrackName
|
|
21261
|
-
);
|
|
21531
|
+
if (tracked) fec.cancelBlockTimeout(sbn, packetId, catalogTrackName);
|
|
21532
|
+
fec.countRecovery(packetId, sbn, catalogTrackName);
|
|
21533
|
+
fec.markBlockCompleted(sbn, packetId, catalogTrackName);
|
|
21262
21534
|
}
|
|
21263
|
-
|
|
21264
|
-
sourceFec.sbn,
|
|
21265
|
-
recovered,
|
|
21266
|
-
info.packetId,
|
|
21267
|
-
catalogTrackName
|
|
21268
|
-
);
|
|
21269
|
-
}
|
|
21535
|
+
});
|
|
21270
21536
|
}
|
|
21271
21537
|
}
|
|
21272
21538
|
}
|
|
@@ -21642,7 +21908,7 @@ var MmtpRouter = class {
|
|
|
21642
21908
|
}
|
|
21643
21909
|
// ── Private: FEC recovery routing ───────────────────────────────
|
|
21644
21910
|
/** Route WASM-recovered block through appropriate path based on container kind. */
|
|
21645
|
-
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName) {
|
|
21911
|
+
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName, complete) {
|
|
21646
21912
|
const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
|
|
21647
21913
|
if (ownerPacketId === void 0) {
|
|
21648
21914
|
throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
|
|
@@ -21659,103 +21925,68 @@ var MmtpRouter = class {
|
|
|
21659
21925
|
ownerPacketId,
|
|
21660
21926
|
blockProfile
|
|
21661
21927
|
});
|
|
21662
|
-
this.#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);
|
|
21663
|
-
}
|
|
21664
|
-
|
|
21665
|
-
|
|
21666
|
-
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
|
|
21671
|
-
|
|
21672
|
-
|
|
21928
|
+
return this.#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete);
|
|
21929
|
+
}
|
|
21930
|
+
/**
|
|
21931
|
+
* A recovered source packet of a protected flow is admitted on its own
|
|
21932
|
+
* digest: its bytes with the SS_ID it was sent with (SS_Start + ESI)
|
|
21933
|
+
* appended are the datagram the record listed (7.2.7). One that no record
|
|
21934
|
+
* lists is dropped; holding it would let it out later as a multicast arrival.
|
|
21935
|
+
* Returns whether the decode recovered the block: every packet it yields
|
|
21936
|
+
* that this router has not parsed already is admitted. A block one of whose
|
|
21937
|
+
* recovered packets is refused is unrecovered (7.2.7): its caller leaves it
|
|
21938
|
+
* open, so it reaches its FEC deadline and fails by class. Always true
|
|
21939
|
+
* outside a protected flow.
|
|
21940
|
+
*/
|
|
21941
|
+
#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete) {
|
|
21942
|
+
const receiver = ownerPacketId == null ? void 0 : this.#provenanceFor(ownerPacketId, catalogTrackName);
|
|
21943
|
+
let recovered = true;
|
|
21944
|
+
if (receiver) {
|
|
21945
|
+
const parsed = this.#stream(ownerPacketId, catalogTrackName).duplicates.parsedSymbols;
|
|
21946
|
+
result = {
|
|
21947
|
+
...result,
|
|
21948
|
+
symbols: result.symbols.filter((sym) => {
|
|
21949
|
+
const ssId = this.#fecAnchorForRecovered(blockId, sym.index);
|
|
21950
|
+
if (parsed.has(ssId)) return false;
|
|
21951
|
+
let admitted = false;
|
|
21952
|
+
if (receiver === "ambiguous") {
|
|
21953
|
+
this.#ambiguousRecoveredDrops++;
|
|
21954
|
+
} else {
|
|
21955
|
+
const wire = new Uint8Array(sym.rawSymbol.byteLength + 4);
|
|
21956
|
+
wire.set(sym.rawSymbol);
|
|
21957
|
+
new DataView(wire.buffer).setUint32(sym.rawSymbol.byteLength, ssId);
|
|
21958
|
+
admitted = receiver.admitRecovered(wire);
|
|
21959
|
+
}
|
|
21960
|
+
if (!admitted) recovered = false;
|
|
21961
|
+
return admitted;
|
|
21962
|
+
})
|
|
21963
|
+
};
|
|
21673
21964
|
}
|
|
21674
|
-
|
|
21675
|
-
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
|
|
21679
|
-
|
|
21680
|
-
|
|
21681
|
-
|
|
21682
|
-
|
|
21683
|
-
|
|
21965
|
+
if (recovered) complete?.();
|
|
21966
|
+
this.#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);
|
|
21967
|
+
return recovered;
|
|
21968
|
+
}
|
|
21969
|
+
/**
|
|
21970
|
+
* The receiver a recovered packet of `packetId` answers to: the flow that
|
|
21971
|
+
* admitted the stream's source datagrams. Before any was admitted, the one
|
|
21972
|
+
* flow whose registration lists the stream; more than one (a repair flow
|
|
21973
|
+
* also lists its owners' source tracks) cannot be told apart, so none.
|
|
21974
|
+
*/
|
|
21975
|
+
#provenanceFor(packetId, catalogTrackName) {
|
|
21976
|
+
const byName = this.#provenanceStreamFlow.get(packetId);
|
|
21977
|
+
const flows = new Set(
|
|
21978
|
+
catalogTrackName === void 0 || !byName ? byName?.values() : byName.has(catalogTrackName) ? [byName.get(catalogTrackName)] : []
|
|
21979
|
+
);
|
|
21980
|
+
if (flows.size > 1) return "ambiguous";
|
|
21981
|
+
for (const bound of flows) return this.#provenance.get(bound)?.receiver ?? "ambiguous";
|
|
21982
|
+
let match;
|
|
21983
|
+
for (const { receiver, ingress } of this.#provenance.values()) {
|
|
21984
|
+
if (ingress.tracks.some((t) => t.packetId === packetId && (catalogTrackName === void 0 || t.name === catalogTrackName))) {
|
|
21985
|
+
if (match) return "ambiguous";
|
|
21986
|
+
match = receiver;
|
|
21684
21987
|
}
|
|
21685
|
-
this.#admitRecoveredResult(
|
|
21686
|
-
next.blockId,
|
|
21687
|
-
next.result,
|
|
21688
|
-
next.ownerPacketId,
|
|
21689
|
-
next.catalogTrackName,
|
|
21690
|
-
() => {
|
|
21691
|
-
queue.shift();
|
|
21692
|
-
drain();
|
|
21693
|
-
}
|
|
21694
|
-
);
|
|
21695
|
-
};
|
|
21696
|
-
drain();
|
|
21697
|
-
}
|
|
21698
|
-
#admitRecoveredResult(blockId, result, ownerPacketId, catalogTrackName, done) {
|
|
21699
|
-
const verifier = this.#manifestVerifier;
|
|
21700
|
-
const resolveIdentity = this.#resolveManifestIdentity;
|
|
21701
|
-
const resolveIdentityForRoute = this.#resolveManifestIdentityForRoute;
|
|
21702
|
-
if (!verifier) {
|
|
21703
|
-
this.#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);
|
|
21704
|
-
done();
|
|
21705
|
-
return;
|
|
21706
21988
|
}
|
|
21707
|
-
|
|
21708
|
-
const symbols = result.symbols;
|
|
21709
|
-
const admitNext = (index) => {
|
|
21710
|
-
const symbol = symbols[index];
|
|
21711
|
-
if (!symbol) {
|
|
21712
|
-
done();
|
|
21713
|
-
return;
|
|
21714
|
-
}
|
|
21715
|
-
const identity = resolveIdentityForRoute ? resolveIdentityForRoute({
|
|
21716
|
-
packetId: ownerPacketId,
|
|
21717
|
-
sbn: blockId,
|
|
21718
|
-
esi: symbol.index,
|
|
21719
|
-
...catalogTrackName === void 0 ? {} : { catalogTrackName },
|
|
21720
|
-
recovered: true,
|
|
21721
|
-
sourceSymbolFormat: "fec-padded-recovery"
|
|
21722
|
-
}) : resolveIdentity(ownerPacketId, blockId, symbol.index, catalogTrackName);
|
|
21723
|
-
if (!identity) {
|
|
21724
|
-
console.warn(`[MmtpRouter] manifest identity unresolved; recovered symbol is fail-open for packetId=${ownerPacketId} block=${blockId} esi=${symbol.index}`);
|
|
21725
|
-
this.#routeProcessedFecSymbols(
|
|
21726
|
-
blockId,
|
|
21727
|
-
{ ...result, symbols: [symbol] },
|
|
21728
|
-
ownerPacketId,
|
|
21729
|
-
catalogTrackName
|
|
21730
|
-
);
|
|
21731
|
-
admitNext(index + 1);
|
|
21732
|
-
return;
|
|
21733
|
-
}
|
|
21734
|
-
const admission = verifier.admitObject(
|
|
21735
|
-
{
|
|
21736
|
-
...identity,
|
|
21737
|
-
packetId: ownerPacketId
|
|
21738
|
-
},
|
|
21739
|
-
symbol.rawSymbol,
|
|
21740
|
-
() => {
|
|
21741
|
-
if (!this.#disposed) {
|
|
21742
|
-
this.#routeProcessedFecSymbols(
|
|
21743
|
-
blockId,
|
|
21744
|
-
{ ...result, symbols: [symbol] },
|
|
21745
|
-
ownerPacketId,
|
|
21746
|
-
catalogTrackName
|
|
21747
|
-
);
|
|
21748
|
-
}
|
|
21749
|
-
admitNext(index + 1);
|
|
21750
|
-
},
|
|
21751
|
-
// A stalled symbol that is later discarded instead of played must still
|
|
21752
|
-
// advance this loop; enforce mode never calls the release callback for
|
|
21753
|
-
// it (BLO-30095).
|
|
21754
|
-
() => admitNext(index + 1)
|
|
21755
|
-
);
|
|
21756
|
-
if (admission.action === "drop") admitNext(index + 1);
|
|
21757
|
-
};
|
|
21758
|
-
admitNext(0);
|
|
21989
|
+
return match;
|
|
21759
21990
|
}
|
|
21760
21991
|
#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName) {
|
|
21761
21992
|
if (ownerPacketId == null) return;
|
|
@@ -22652,10 +22883,11 @@ var MulticastTransport = class {
|
|
|
22652
22883
|
try {
|
|
22653
22884
|
const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
22654
22885
|
console.log(
|
|
22655
|
-
`[MulticastTransport] window.multicast.subscribe(${config.sourceAddress}, ${config.groupAddress}, ${config.port})`
|
|
22886
|
+
`[MulticastTransport] window.multicast.subscribe(${config.sourceAddress ?? config.senderAddress ?? "*"}, ${config.groupAddress}, ${config.port})`
|
|
22656
22887
|
);
|
|
22657
22888
|
const subscribeConfig = toWindowMulticastSubscribeConfig({
|
|
22658
22889
|
source: config.sourceAddress,
|
|
22890
|
+
senderAddress: config.senderAddress,
|
|
22659
22891
|
group: config.groupAddress,
|
|
22660
22892
|
port: config.port,
|
|
22661
22893
|
mode: config.mode,
|
|
@@ -23251,19 +23483,2739 @@ var MulticastTransport = class {
|
|
|
23251
23483
|
}
|
|
23252
23484
|
};
|
|
23253
23485
|
|
|
23254
|
-
//
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23486
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/crypto.js
|
|
23487
|
+
var crypto2 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0;
|
|
23488
|
+
|
|
23489
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/utils.js
|
|
23490
|
+
function isBytes(a) {
|
|
23491
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
23492
|
+
}
|
|
23493
|
+
function anumber(n) {
|
|
23494
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
23495
|
+
throw new Error("positive integer expected, got " + n);
|
|
23496
|
+
}
|
|
23497
|
+
function abytes(b, ...lengths) {
|
|
23498
|
+
if (!isBytes(b))
|
|
23499
|
+
throw new Error("Uint8Array expected");
|
|
23500
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
23501
|
+
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
23502
|
+
}
|
|
23503
|
+
function aexists(instance, checkFinished = true) {
|
|
23504
|
+
if (instance.destroyed)
|
|
23505
|
+
throw new Error("Hash instance has been destroyed");
|
|
23506
|
+
if (checkFinished && instance.finished)
|
|
23507
|
+
throw new Error("Hash#digest() has already been called");
|
|
23508
|
+
}
|
|
23509
|
+
function aoutput(out, instance) {
|
|
23510
|
+
abytes(out);
|
|
23511
|
+
const min = instance.outputLen;
|
|
23512
|
+
if (out.length < min) {
|
|
23513
|
+
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
23514
|
+
}
|
|
23515
|
+
}
|
|
23516
|
+
function clean(...arrays) {
|
|
23517
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
23518
|
+
arrays[i].fill(0);
|
|
23519
|
+
}
|
|
23520
|
+
}
|
|
23521
|
+
function createView(arr) {
|
|
23522
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
23523
|
+
}
|
|
23524
|
+
function rotr(word, shift) {
|
|
23525
|
+
return word << 32 - shift | word >>> shift;
|
|
23526
|
+
}
|
|
23527
|
+
var hasHexBuiltin = /* @__PURE__ */ (() => (
|
|
23528
|
+
// @ts-ignore
|
|
23529
|
+
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|
23530
|
+
))();
|
|
23531
|
+
var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
23532
|
+
function bytesToHex(bytes) {
|
|
23533
|
+
abytes(bytes);
|
|
23534
|
+
if (hasHexBuiltin)
|
|
23535
|
+
return bytes.toHex();
|
|
23536
|
+
let hex3 = "";
|
|
23537
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
23538
|
+
hex3 += hexes[bytes[i]];
|
|
23539
|
+
}
|
|
23540
|
+
return hex3;
|
|
23541
|
+
}
|
|
23542
|
+
var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
|
|
23543
|
+
function asciiToBase16(ch) {
|
|
23544
|
+
if (ch >= asciis._0 && ch <= asciis._9)
|
|
23545
|
+
return ch - asciis._0;
|
|
23546
|
+
if (ch >= asciis.A && ch <= asciis.F)
|
|
23547
|
+
return ch - (asciis.A - 10);
|
|
23548
|
+
if (ch >= asciis.a && ch <= asciis.f)
|
|
23549
|
+
return ch - (asciis.a - 10);
|
|
23550
|
+
return;
|
|
23551
|
+
}
|
|
23552
|
+
function hexToBytes(hex3) {
|
|
23553
|
+
if (typeof hex3 !== "string")
|
|
23554
|
+
throw new Error("hex string expected, got " + typeof hex3);
|
|
23555
|
+
if (hasHexBuiltin)
|
|
23556
|
+
return Uint8Array.fromHex(hex3);
|
|
23557
|
+
const hl = hex3.length;
|
|
23558
|
+
const al = hl / 2;
|
|
23559
|
+
if (hl % 2)
|
|
23560
|
+
throw new Error("hex string expected, got unpadded hex of length " + hl);
|
|
23561
|
+
const array = new Uint8Array(al);
|
|
23562
|
+
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
23563
|
+
const n1 = asciiToBase16(hex3.charCodeAt(hi));
|
|
23564
|
+
const n2 = asciiToBase16(hex3.charCodeAt(hi + 1));
|
|
23565
|
+
if (n1 === void 0 || n2 === void 0) {
|
|
23566
|
+
const char = hex3[hi] + hex3[hi + 1];
|
|
23567
|
+
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
23568
|
+
}
|
|
23569
|
+
array[ai] = n1 * 16 + n2;
|
|
23570
|
+
}
|
|
23571
|
+
return array;
|
|
23572
|
+
}
|
|
23573
|
+
function utf8ToBytes(str) {
|
|
23574
|
+
if (typeof str !== "string")
|
|
23575
|
+
throw new Error("string expected");
|
|
23576
|
+
return new Uint8Array(new TextEncoder().encode(str));
|
|
23577
|
+
}
|
|
23578
|
+
function toBytes(data) {
|
|
23579
|
+
if (typeof data === "string")
|
|
23580
|
+
data = utf8ToBytes(data);
|
|
23581
|
+
abytes(data);
|
|
23582
|
+
return data;
|
|
23583
|
+
}
|
|
23584
|
+
function concatBytes(...arrays) {
|
|
23585
|
+
let sum = 0;
|
|
23586
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
23587
|
+
const a = arrays[i];
|
|
23588
|
+
abytes(a);
|
|
23589
|
+
sum += a.length;
|
|
23590
|
+
}
|
|
23591
|
+
const res = new Uint8Array(sum);
|
|
23592
|
+
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
23593
|
+
const a = arrays[i];
|
|
23594
|
+
res.set(a, pad);
|
|
23595
|
+
pad += a.length;
|
|
23596
|
+
}
|
|
23597
|
+
return res;
|
|
23598
|
+
}
|
|
23599
|
+
var Hash = class {
|
|
23600
|
+
};
|
|
23601
|
+
function createHasher(hashCons) {
|
|
23602
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
23603
|
+
const tmp = hashCons();
|
|
23604
|
+
hashC.outputLen = tmp.outputLen;
|
|
23605
|
+
hashC.blockLen = tmp.blockLen;
|
|
23606
|
+
hashC.create = () => hashCons();
|
|
23607
|
+
return hashC;
|
|
23258
23608
|
}
|
|
23259
|
-
function
|
|
23260
|
-
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
|
|
23609
|
+
function randomBytes(bytesLength = 32) {
|
|
23610
|
+
if (crypto2 && typeof crypto2.getRandomValues === "function") {
|
|
23611
|
+
return crypto2.getRandomValues(new Uint8Array(bytesLength));
|
|
23612
|
+
}
|
|
23613
|
+
if (crypto2 && typeof crypto2.randomBytes === "function") {
|
|
23614
|
+
return Uint8Array.from(crypto2.randomBytes(bytesLength));
|
|
23615
|
+
}
|
|
23616
|
+
throw new Error("crypto.getRandomValues must be defined");
|
|
23617
|
+
}
|
|
23618
|
+
|
|
23619
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/_md.js
|
|
23620
|
+
function setBigUint64(view2, byteOffset, value, isLE) {
|
|
23621
|
+
if (typeof view2.setBigUint64 === "function")
|
|
23622
|
+
return view2.setBigUint64(byteOffset, value, isLE);
|
|
23623
|
+
const _32n2 = BigInt(32);
|
|
23624
|
+
const _u32_max = BigInt(4294967295);
|
|
23625
|
+
const wh = Number(value >> _32n2 & _u32_max);
|
|
23626
|
+
const wl = Number(value & _u32_max);
|
|
23627
|
+
const h = isLE ? 4 : 0;
|
|
23628
|
+
const l = isLE ? 0 : 4;
|
|
23629
|
+
view2.setUint32(byteOffset + h, wh, isLE);
|
|
23630
|
+
view2.setUint32(byteOffset + l, wl, isLE);
|
|
23631
|
+
}
|
|
23632
|
+
function Chi(a, b, c) {
|
|
23633
|
+
return a & b ^ ~a & c;
|
|
23634
|
+
}
|
|
23635
|
+
function Maj(a, b, c) {
|
|
23636
|
+
return a & b ^ a & c ^ b & c;
|
|
23637
|
+
}
|
|
23638
|
+
var HashMD = class extends Hash {
|
|
23639
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
23640
|
+
super();
|
|
23641
|
+
this.finished = false;
|
|
23642
|
+
this.length = 0;
|
|
23643
|
+
this.pos = 0;
|
|
23644
|
+
this.destroyed = false;
|
|
23645
|
+
this.blockLen = blockLen;
|
|
23646
|
+
this.outputLen = outputLen;
|
|
23647
|
+
this.padOffset = padOffset;
|
|
23648
|
+
this.isLE = isLE;
|
|
23649
|
+
this.buffer = new Uint8Array(blockLen);
|
|
23650
|
+
this.view = createView(this.buffer);
|
|
23651
|
+
}
|
|
23652
|
+
update(data) {
|
|
23653
|
+
aexists(this);
|
|
23654
|
+
data = toBytes(data);
|
|
23655
|
+
abytes(data);
|
|
23656
|
+
const { view: view2, buffer, blockLen } = this;
|
|
23657
|
+
const len = data.length;
|
|
23658
|
+
for (let pos = 0; pos < len; ) {
|
|
23659
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
23660
|
+
if (take === blockLen) {
|
|
23661
|
+
const dataView = createView(data);
|
|
23662
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
23663
|
+
this.process(dataView, pos);
|
|
23664
|
+
continue;
|
|
23665
|
+
}
|
|
23666
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
23667
|
+
this.pos += take;
|
|
23668
|
+
pos += take;
|
|
23669
|
+
if (this.pos === blockLen) {
|
|
23670
|
+
this.process(view2, 0);
|
|
23671
|
+
this.pos = 0;
|
|
23672
|
+
}
|
|
23673
|
+
}
|
|
23674
|
+
this.length += data.length;
|
|
23675
|
+
this.roundClean();
|
|
23676
|
+
return this;
|
|
23677
|
+
}
|
|
23678
|
+
digestInto(out) {
|
|
23679
|
+
aexists(this);
|
|
23680
|
+
aoutput(out, this);
|
|
23681
|
+
this.finished = true;
|
|
23682
|
+
const { buffer, view: view2, blockLen, isLE } = this;
|
|
23683
|
+
let { pos } = this;
|
|
23684
|
+
buffer[pos++] = 128;
|
|
23685
|
+
clean(this.buffer.subarray(pos));
|
|
23686
|
+
if (this.padOffset > blockLen - pos) {
|
|
23687
|
+
this.process(view2, 0);
|
|
23688
|
+
pos = 0;
|
|
23689
|
+
}
|
|
23690
|
+
for (let i = pos; i < blockLen; i++)
|
|
23691
|
+
buffer[i] = 0;
|
|
23692
|
+
setBigUint64(view2, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
23693
|
+
this.process(view2, 0);
|
|
23694
|
+
const oview = createView(out);
|
|
23695
|
+
const len = this.outputLen;
|
|
23696
|
+
if (len % 4)
|
|
23697
|
+
throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
23698
|
+
const outLen = len / 4;
|
|
23699
|
+
const state = this.get();
|
|
23700
|
+
if (outLen > state.length)
|
|
23701
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
23702
|
+
for (let i = 0; i < outLen; i++)
|
|
23703
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
23704
|
+
}
|
|
23705
|
+
digest() {
|
|
23706
|
+
const { buffer, outputLen } = this;
|
|
23707
|
+
this.digestInto(buffer);
|
|
23708
|
+
const res = buffer.slice(0, outputLen);
|
|
23709
|
+
this.destroy();
|
|
23710
|
+
return res;
|
|
23711
|
+
}
|
|
23712
|
+
_cloneInto(to) {
|
|
23713
|
+
to || (to = new this.constructor());
|
|
23714
|
+
to.set(...this.get());
|
|
23715
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
23716
|
+
to.destroyed = destroyed;
|
|
23717
|
+
to.finished = finished;
|
|
23718
|
+
to.length = length;
|
|
23719
|
+
to.pos = pos;
|
|
23720
|
+
if (length % blockLen)
|
|
23721
|
+
to.buffer.set(buffer);
|
|
23722
|
+
return to;
|
|
23723
|
+
}
|
|
23724
|
+
clone() {
|
|
23725
|
+
return this._cloneInto();
|
|
23726
|
+
}
|
|
23727
|
+
};
|
|
23728
|
+
var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
23729
|
+
1779033703,
|
|
23730
|
+
3144134277,
|
|
23731
|
+
1013904242,
|
|
23732
|
+
2773480762,
|
|
23733
|
+
1359893119,
|
|
23734
|
+
2600822924,
|
|
23735
|
+
528734635,
|
|
23736
|
+
1541459225
|
|
23737
|
+
]);
|
|
23738
|
+
var SHA512_IV = /* @__PURE__ */ Uint32Array.from([
|
|
23739
|
+
1779033703,
|
|
23740
|
+
4089235720,
|
|
23741
|
+
3144134277,
|
|
23742
|
+
2227873595,
|
|
23743
|
+
1013904242,
|
|
23744
|
+
4271175723,
|
|
23745
|
+
2773480762,
|
|
23746
|
+
1595750129,
|
|
23747
|
+
1359893119,
|
|
23748
|
+
2917565137,
|
|
23749
|
+
2600822924,
|
|
23750
|
+
725511199,
|
|
23751
|
+
528734635,
|
|
23752
|
+
4215389547,
|
|
23753
|
+
1541459225,
|
|
23754
|
+
327033209
|
|
23755
|
+
]);
|
|
23756
|
+
|
|
23757
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/_u64.js
|
|
23758
|
+
var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
23759
|
+
var _32n = /* @__PURE__ */ BigInt(32);
|
|
23760
|
+
function fromBig(n, le = false) {
|
|
23761
|
+
if (le)
|
|
23762
|
+
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
|
|
23763
|
+
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
23764
|
+
}
|
|
23765
|
+
function split(lst, le = false) {
|
|
23766
|
+
const len = lst.length;
|
|
23767
|
+
let Ah = new Uint32Array(len);
|
|
23768
|
+
let Al = new Uint32Array(len);
|
|
23769
|
+
for (let i = 0; i < len; i++) {
|
|
23770
|
+
const { h, l } = fromBig(lst[i], le);
|
|
23771
|
+
[Ah[i], Al[i]] = [h, l];
|
|
23772
|
+
}
|
|
23773
|
+
return [Ah, Al];
|
|
23774
|
+
}
|
|
23775
|
+
var shrSH = (h, _l, s) => h >>> s;
|
|
23776
|
+
var shrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
23777
|
+
var rotrSH = (h, l, s) => h >>> s | l << 32 - s;
|
|
23778
|
+
var rotrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
23779
|
+
var rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32;
|
|
23780
|
+
var rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s;
|
|
23781
|
+
function add(Ah, Al, Bh, Bl) {
|
|
23782
|
+
const l = (Al >>> 0) + (Bl >>> 0);
|
|
23783
|
+
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
|
|
23784
|
+
}
|
|
23785
|
+
var add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
|
|
23786
|
+
var add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
|
|
23787
|
+
var add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
|
|
23788
|
+
var add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
|
|
23789
|
+
var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
|
|
23790
|
+
var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
|
|
23791
|
+
|
|
23792
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/sha2.js
|
|
23793
|
+
var SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
23794
|
+
1116352408,
|
|
23795
|
+
1899447441,
|
|
23796
|
+
3049323471,
|
|
23797
|
+
3921009573,
|
|
23798
|
+
961987163,
|
|
23799
|
+
1508970993,
|
|
23800
|
+
2453635748,
|
|
23801
|
+
2870763221,
|
|
23802
|
+
3624381080,
|
|
23803
|
+
310598401,
|
|
23804
|
+
607225278,
|
|
23805
|
+
1426881987,
|
|
23806
|
+
1925078388,
|
|
23807
|
+
2162078206,
|
|
23808
|
+
2614888103,
|
|
23809
|
+
3248222580,
|
|
23810
|
+
3835390401,
|
|
23811
|
+
4022224774,
|
|
23812
|
+
264347078,
|
|
23813
|
+
604807628,
|
|
23814
|
+
770255983,
|
|
23815
|
+
1249150122,
|
|
23816
|
+
1555081692,
|
|
23817
|
+
1996064986,
|
|
23818
|
+
2554220882,
|
|
23819
|
+
2821834349,
|
|
23820
|
+
2952996808,
|
|
23821
|
+
3210313671,
|
|
23822
|
+
3336571891,
|
|
23823
|
+
3584528711,
|
|
23824
|
+
113926993,
|
|
23825
|
+
338241895,
|
|
23826
|
+
666307205,
|
|
23827
|
+
773529912,
|
|
23828
|
+
1294757372,
|
|
23829
|
+
1396182291,
|
|
23830
|
+
1695183700,
|
|
23831
|
+
1986661051,
|
|
23832
|
+
2177026350,
|
|
23833
|
+
2456956037,
|
|
23834
|
+
2730485921,
|
|
23835
|
+
2820302411,
|
|
23836
|
+
3259730800,
|
|
23837
|
+
3345764771,
|
|
23838
|
+
3516065817,
|
|
23839
|
+
3600352804,
|
|
23840
|
+
4094571909,
|
|
23841
|
+
275423344,
|
|
23842
|
+
430227734,
|
|
23843
|
+
506948616,
|
|
23844
|
+
659060556,
|
|
23845
|
+
883997877,
|
|
23846
|
+
958139571,
|
|
23847
|
+
1322822218,
|
|
23848
|
+
1537002063,
|
|
23849
|
+
1747873779,
|
|
23850
|
+
1955562222,
|
|
23851
|
+
2024104815,
|
|
23852
|
+
2227730452,
|
|
23853
|
+
2361852424,
|
|
23854
|
+
2428436474,
|
|
23855
|
+
2756734187,
|
|
23856
|
+
3204031479,
|
|
23857
|
+
3329325298
|
|
23858
|
+
]);
|
|
23859
|
+
var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
23860
|
+
var SHA256 = class extends HashMD {
|
|
23861
|
+
constructor(outputLen = 32) {
|
|
23862
|
+
super(64, outputLen, 8, false);
|
|
23863
|
+
this.A = SHA256_IV[0] | 0;
|
|
23864
|
+
this.B = SHA256_IV[1] | 0;
|
|
23865
|
+
this.C = SHA256_IV[2] | 0;
|
|
23866
|
+
this.D = SHA256_IV[3] | 0;
|
|
23867
|
+
this.E = SHA256_IV[4] | 0;
|
|
23868
|
+
this.F = SHA256_IV[5] | 0;
|
|
23869
|
+
this.G = SHA256_IV[6] | 0;
|
|
23870
|
+
this.H = SHA256_IV[7] | 0;
|
|
23871
|
+
}
|
|
23872
|
+
get() {
|
|
23873
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
23874
|
+
return [A, B, C, D, E, F, G, H];
|
|
23875
|
+
}
|
|
23876
|
+
// prettier-ignore
|
|
23877
|
+
set(A, B, C, D, E, F, G, H) {
|
|
23878
|
+
this.A = A | 0;
|
|
23879
|
+
this.B = B | 0;
|
|
23880
|
+
this.C = C | 0;
|
|
23881
|
+
this.D = D | 0;
|
|
23882
|
+
this.E = E | 0;
|
|
23883
|
+
this.F = F | 0;
|
|
23884
|
+
this.G = G | 0;
|
|
23885
|
+
this.H = H | 0;
|
|
23886
|
+
}
|
|
23887
|
+
process(view2, offset) {
|
|
23888
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
23889
|
+
SHA256_W[i] = view2.getUint32(offset, false);
|
|
23890
|
+
for (let i = 16; i < 64; i++) {
|
|
23891
|
+
const W15 = SHA256_W[i - 15];
|
|
23892
|
+
const W2 = SHA256_W[i - 2];
|
|
23893
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
23894
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
23895
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
23896
|
+
}
|
|
23897
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
23898
|
+
for (let i = 0; i < 64; i++) {
|
|
23899
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
23900
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
23901
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
23902
|
+
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
23903
|
+
H = G;
|
|
23904
|
+
G = F;
|
|
23905
|
+
F = E;
|
|
23906
|
+
E = D + T1 | 0;
|
|
23907
|
+
D = C;
|
|
23908
|
+
C = B;
|
|
23909
|
+
B = A;
|
|
23910
|
+
A = T1 + T2 | 0;
|
|
23911
|
+
}
|
|
23912
|
+
A = A + this.A | 0;
|
|
23913
|
+
B = B + this.B | 0;
|
|
23914
|
+
C = C + this.C | 0;
|
|
23915
|
+
D = D + this.D | 0;
|
|
23916
|
+
E = E + this.E | 0;
|
|
23917
|
+
F = F + this.F | 0;
|
|
23918
|
+
G = G + this.G | 0;
|
|
23919
|
+
H = H + this.H | 0;
|
|
23920
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
23921
|
+
}
|
|
23922
|
+
roundClean() {
|
|
23923
|
+
clean(SHA256_W);
|
|
23924
|
+
}
|
|
23925
|
+
destroy() {
|
|
23926
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
23927
|
+
clean(this.buffer);
|
|
23928
|
+
}
|
|
23929
|
+
};
|
|
23930
|
+
var K512 = /* @__PURE__ */ (() => split([
|
|
23931
|
+
"0x428a2f98d728ae22",
|
|
23932
|
+
"0x7137449123ef65cd",
|
|
23933
|
+
"0xb5c0fbcfec4d3b2f",
|
|
23934
|
+
"0xe9b5dba58189dbbc",
|
|
23935
|
+
"0x3956c25bf348b538",
|
|
23936
|
+
"0x59f111f1b605d019",
|
|
23937
|
+
"0x923f82a4af194f9b",
|
|
23938
|
+
"0xab1c5ed5da6d8118",
|
|
23939
|
+
"0xd807aa98a3030242",
|
|
23940
|
+
"0x12835b0145706fbe",
|
|
23941
|
+
"0x243185be4ee4b28c",
|
|
23942
|
+
"0x550c7dc3d5ffb4e2",
|
|
23943
|
+
"0x72be5d74f27b896f",
|
|
23944
|
+
"0x80deb1fe3b1696b1",
|
|
23945
|
+
"0x9bdc06a725c71235",
|
|
23946
|
+
"0xc19bf174cf692694",
|
|
23947
|
+
"0xe49b69c19ef14ad2",
|
|
23948
|
+
"0xefbe4786384f25e3",
|
|
23949
|
+
"0x0fc19dc68b8cd5b5",
|
|
23950
|
+
"0x240ca1cc77ac9c65",
|
|
23951
|
+
"0x2de92c6f592b0275",
|
|
23952
|
+
"0x4a7484aa6ea6e483",
|
|
23953
|
+
"0x5cb0a9dcbd41fbd4",
|
|
23954
|
+
"0x76f988da831153b5",
|
|
23955
|
+
"0x983e5152ee66dfab",
|
|
23956
|
+
"0xa831c66d2db43210",
|
|
23957
|
+
"0xb00327c898fb213f",
|
|
23958
|
+
"0xbf597fc7beef0ee4",
|
|
23959
|
+
"0xc6e00bf33da88fc2",
|
|
23960
|
+
"0xd5a79147930aa725",
|
|
23961
|
+
"0x06ca6351e003826f",
|
|
23962
|
+
"0x142929670a0e6e70",
|
|
23963
|
+
"0x27b70a8546d22ffc",
|
|
23964
|
+
"0x2e1b21385c26c926",
|
|
23965
|
+
"0x4d2c6dfc5ac42aed",
|
|
23966
|
+
"0x53380d139d95b3df",
|
|
23967
|
+
"0x650a73548baf63de",
|
|
23968
|
+
"0x766a0abb3c77b2a8",
|
|
23969
|
+
"0x81c2c92e47edaee6",
|
|
23970
|
+
"0x92722c851482353b",
|
|
23971
|
+
"0xa2bfe8a14cf10364",
|
|
23972
|
+
"0xa81a664bbc423001",
|
|
23973
|
+
"0xc24b8b70d0f89791",
|
|
23974
|
+
"0xc76c51a30654be30",
|
|
23975
|
+
"0xd192e819d6ef5218",
|
|
23976
|
+
"0xd69906245565a910",
|
|
23977
|
+
"0xf40e35855771202a",
|
|
23978
|
+
"0x106aa07032bbd1b8",
|
|
23979
|
+
"0x19a4c116b8d2d0c8",
|
|
23980
|
+
"0x1e376c085141ab53",
|
|
23981
|
+
"0x2748774cdf8eeb99",
|
|
23982
|
+
"0x34b0bcb5e19b48a8",
|
|
23983
|
+
"0x391c0cb3c5c95a63",
|
|
23984
|
+
"0x4ed8aa4ae3418acb",
|
|
23985
|
+
"0x5b9cca4f7763e373",
|
|
23986
|
+
"0x682e6ff3d6b2b8a3",
|
|
23987
|
+
"0x748f82ee5defb2fc",
|
|
23988
|
+
"0x78a5636f43172f60",
|
|
23989
|
+
"0x84c87814a1f0ab72",
|
|
23990
|
+
"0x8cc702081a6439ec",
|
|
23991
|
+
"0x90befffa23631e28",
|
|
23992
|
+
"0xa4506cebde82bde9",
|
|
23993
|
+
"0xbef9a3f7b2c67915",
|
|
23994
|
+
"0xc67178f2e372532b",
|
|
23995
|
+
"0xca273eceea26619c",
|
|
23996
|
+
"0xd186b8c721c0c207",
|
|
23997
|
+
"0xeada7dd6cde0eb1e",
|
|
23998
|
+
"0xf57d4f7fee6ed178",
|
|
23999
|
+
"0x06f067aa72176fba",
|
|
24000
|
+
"0x0a637dc5a2c898a6",
|
|
24001
|
+
"0x113f9804bef90dae",
|
|
24002
|
+
"0x1b710b35131c471b",
|
|
24003
|
+
"0x28db77f523047d84",
|
|
24004
|
+
"0x32caab7b40c72493",
|
|
24005
|
+
"0x3c9ebe0a15c9bebc",
|
|
24006
|
+
"0x431d67c49c100d4c",
|
|
24007
|
+
"0x4cc5d4becb3e42b6",
|
|
24008
|
+
"0x597f299cfc657e2a",
|
|
24009
|
+
"0x5fcb6fab3ad6faec",
|
|
24010
|
+
"0x6c44198c4a475817"
|
|
24011
|
+
].map((n) => BigInt(n))))();
|
|
24012
|
+
var SHA512_Kh = /* @__PURE__ */ (() => K512[0])();
|
|
24013
|
+
var SHA512_Kl = /* @__PURE__ */ (() => K512[1])();
|
|
24014
|
+
var SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
|
|
24015
|
+
var SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
|
|
24016
|
+
var SHA512 = class extends HashMD {
|
|
24017
|
+
constructor(outputLen = 64) {
|
|
24018
|
+
super(128, outputLen, 16, false);
|
|
24019
|
+
this.Ah = SHA512_IV[0] | 0;
|
|
24020
|
+
this.Al = SHA512_IV[1] | 0;
|
|
24021
|
+
this.Bh = SHA512_IV[2] | 0;
|
|
24022
|
+
this.Bl = SHA512_IV[3] | 0;
|
|
24023
|
+
this.Ch = SHA512_IV[4] | 0;
|
|
24024
|
+
this.Cl = SHA512_IV[5] | 0;
|
|
24025
|
+
this.Dh = SHA512_IV[6] | 0;
|
|
24026
|
+
this.Dl = SHA512_IV[7] | 0;
|
|
24027
|
+
this.Eh = SHA512_IV[8] | 0;
|
|
24028
|
+
this.El = SHA512_IV[9] | 0;
|
|
24029
|
+
this.Fh = SHA512_IV[10] | 0;
|
|
24030
|
+
this.Fl = SHA512_IV[11] | 0;
|
|
24031
|
+
this.Gh = SHA512_IV[12] | 0;
|
|
24032
|
+
this.Gl = SHA512_IV[13] | 0;
|
|
24033
|
+
this.Hh = SHA512_IV[14] | 0;
|
|
24034
|
+
this.Hl = SHA512_IV[15] | 0;
|
|
24035
|
+
}
|
|
24036
|
+
// prettier-ignore
|
|
24037
|
+
get() {
|
|
24038
|
+
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
24039
|
+
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
|
|
24040
|
+
}
|
|
24041
|
+
// prettier-ignore
|
|
24042
|
+
set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
|
|
24043
|
+
this.Ah = Ah | 0;
|
|
24044
|
+
this.Al = Al | 0;
|
|
24045
|
+
this.Bh = Bh | 0;
|
|
24046
|
+
this.Bl = Bl | 0;
|
|
24047
|
+
this.Ch = Ch | 0;
|
|
24048
|
+
this.Cl = Cl | 0;
|
|
24049
|
+
this.Dh = Dh | 0;
|
|
24050
|
+
this.Dl = Dl | 0;
|
|
24051
|
+
this.Eh = Eh | 0;
|
|
24052
|
+
this.El = El | 0;
|
|
24053
|
+
this.Fh = Fh | 0;
|
|
24054
|
+
this.Fl = Fl | 0;
|
|
24055
|
+
this.Gh = Gh | 0;
|
|
24056
|
+
this.Gl = Gl | 0;
|
|
24057
|
+
this.Hh = Hh | 0;
|
|
24058
|
+
this.Hl = Hl | 0;
|
|
24059
|
+
}
|
|
24060
|
+
process(view2, offset) {
|
|
24061
|
+
for (let i = 0; i < 16; i++, offset += 4) {
|
|
24062
|
+
SHA512_W_H[i] = view2.getUint32(offset);
|
|
24063
|
+
SHA512_W_L[i] = view2.getUint32(offset += 4);
|
|
24064
|
+
}
|
|
24065
|
+
for (let i = 16; i < 80; i++) {
|
|
24066
|
+
const W15h = SHA512_W_H[i - 15] | 0;
|
|
24067
|
+
const W15l = SHA512_W_L[i - 15] | 0;
|
|
24068
|
+
const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7);
|
|
24069
|
+
const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7);
|
|
24070
|
+
const W2h = SHA512_W_H[i - 2] | 0;
|
|
24071
|
+
const W2l = SHA512_W_L[i - 2] | 0;
|
|
24072
|
+
const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6);
|
|
24073
|
+
const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6);
|
|
24074
|
+
const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
|
|
24075
|
+
const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
|
|
24076
|
+
SHA512_W_H[i] = SUMh | 0;
|
|
24077
|
+
SHA512_W_L[i] = SUMl | 0;
|
|
24078
|
+
}
|
|
24079
|
+
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
24080
|
+
for (let i = 0; i < 80; i++) {
|
|
24081
|
+
const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41);
|
|
24082
|
+
const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41);
|
|
24083
|
+
const CHIh = Eh & Fh ^ ~Eh & Gh;
|
|
24084
|
+
const CHIl = El & Fl ^ ~El & Gl;
|
|
24085
|
+
const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
|
|
24086
|
+
const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
|
|
24087
|
+
const T1l = T1ll | 0;
|
|
24088
|
+
const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39);
|
|
24089
|
+
const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39);
|
|
24090
|
+
const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
|
|
24091
|
+
const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
|
|
24092
|
+
Hh = Gh | 0;
|
|
24093
|
+
Hl = Gl | 0;
|
|
24094
|
+
Gh = Fh | 0;
|
|
24095
|
+
Gl = Fl | 0;
|
|
24096
|
+
Fh = Eh | 0;
|
|
24097
|
+
Fl = El | 0;
|
|
24098
|
+
({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
|
|
24099
|
+
Dh = Ch | 0;
|
|
24100
|
+
Dl = Cl | 0;
|
|
24101
|
+
Ch = Bh | 0;
|
|
24102
|
+
Cl = Bl | 0;
|
|
24103
|
+
Bh = Ah | 0;
|
|
24104
|
+
Bl = Al | 0;
|
|
24105
|
+
const All = add3L(T1l, sigma0l, MAJl);
|
|
24106
|
+
Ah = add3H(All, T1h, sigma0h, MAJh);
|
|
24107
|
+
Al = All | 0;
|
|
24108
|
+
}
|
|
24109
|
+
({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
|
|
24110
|
+
({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
|
|
24111
|
+
({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
|
|
24112
|
+
({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
|
|
24113
|
+
({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
|
|
24114
|
+
({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
|
|
24115
|
+
({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
|
|
24116
|
+
({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
|
|
24117
|
+
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
|
|
24118
|
+
}
|
|
24119
|
+
roundClean() {
|
|
24120
|
+
clean(SHA512_W_H, SHA512_W_L);
|
|
24121
|
+
}
|
|
24122
|
+
destroy() {
|
|
24123
|
+
clean(this.buffer);
|
|
24124
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
24125
|
+
}
|
|
24126
|
+
};
|
|
24127
|
+
var sha256 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
24128
|
+
var sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
|
|
24129
|
+
|
|
24130
|
+
// ../mmt-manifest-verify/node_modules/@noble/hashes/esm/sha256.js
|
|
24131
|
+
var sha2562 = sha256;
|
|
24132
|
+
|
|
24133
|
+
// ../mmt-manifest-verify/dist/bc-provenance-v2.js
|
|
24134
|
+
var BCPV2_HEADER_LENGTH = 68;
|
|
24135
|
+
var BCPV2_SIGNATURE_LENGTH = 64;
|
|
24136
|
+
var BCPV2_MIN_RECORD_LENGTH = BCPV2_HEADER_LENGTH + 32 + BCPV2_SIGNATURE_LENGTH;
|
|
24137
|
+
var BCPV2_MAX_RECORD_LENGTH = 1316;
|
|
24138
|
+
var BCPV2_MAX_DIGESTS = 37;
|
|
24139
|
+
var BCPV2_HASH_ALG_SHA256 = 1;
|
|
24140
|
+
var text = new TextEncoder();
|
|
24141
|
+
var MAGIC = Uint8Array.from([77, 72, 77, 50]);
|
|
24142
|
+
var SIGNING_DOMAIN = text.encode("mcast-manifest-v2\0");
|
|
24143
|
+
function equalBytes(left, right) {
|
|
24144
|
+
if (left.byteLength !== right.byteLength)
|
|
24145
|
+
return false;
|
|
24146
|
+
let different = 0;
|
|
24147
|
+
for (let index = 0; index < left.byteLength; index++)
|
|
24148
|
+
different |= left[index] ^ right[index];
|
|
24149
|
+
return different === 0;
|
|
24150
|
+
}
|
|
24151
|
+
function u16(value) {
|
|
24152
|
+
const result = new Uint8Array(2);
|
|
24153
|
+
new DataView(result.buffer).setUint16(0, value);
|
|
24154
|
+
return result;
|
|
24155
|
+
}
|
|
24156
|
+
function u322(value) {
|
|
24157
|
+
const result = new Uint8Array(4);
|
|
24158
|
+
new DataView(result.buffer).setUint32(0, value);
|
|
24159
|
+
return result;
|
|
24160
|
+
}
|
|
24161
|
+
function validU32(value) {
|
|
24162
|
+
return Number.isInteger(value) && value >= 0 && value <= 4294967295;
|
|
24163
|
+
}
|
|
24164
|
+
function validPort(value) {
|
|
24165
|
+
return Number.isInteger(value) && value >= 0 && value <= 65535;
|
|
24166
|
+
}
|
|
24167
|
+
function parseIpv4(address) {
|
|
24168
|
+
const fields = address.split(".");
|
|
24169
|
+
if (fields.length !== 4 || fields.some((field) => !/^\d+$/.test(field))) {
|
|
24170
|
+
throw new Error(`invalid IPv4 address ${address}`);
|
|
24171
|
+
}
|
|
24172
|
+
const values = fields.map(Number);
|
|
24173
|
+
if (values.some((value, index) => value > 255 || String(value) !== fields[index])) {
|
|
24174
|
+
throw new Error(`invalid IPv4 address ${address}`);
|
|
24175
|
+
}
|
|
24176
|
+
return Uint8Array.from(values);
|
|
24177
|
+
}
|
|
24178
|
+
function parseIpv6(address) {
|
|
24179
|
+
const compressed = address.includes("::");
|
|
24180
|
+
if ((address.match(/::/g) ?? []).length > 1)
|
|
24181
|
+
throw new Error(`invalid IPv6 address ${address}`);
|
|
24182
|
+
const [leftText, rightText = ""] = address.split("::");
|
|
24183
|
+
const left = leftText ? leftText.split(":") : [];
|
|
24184
|
+
const right = rightText ? rightText.split(":") : [];
|
|
24185
|
+
if (left.some((group) => group.includes(".")) || right.some((group) => group.includes("."))) {
|
|
24186
|
+
throw new Error(`invalid IPv6 address ${address}`);
|
|
24187
|
+
}
|
|
24188
|
+
const missing = 8 - left.length - right.length;
|
|
24189
|
+
if (missing < 0 || !compressed && missing !== 0 || compressed && missing < 1) {
|
|
24190
|
+
throw new Error(`invalid IPv6 address ${address}`);
|
|
24191
|
+
}
|
|
24192
|
+
const groups = [...left, ...Array.from({ length: missing }, () => "0"), ...right];
|
|
24193
|
+
if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/i.test(group))) {
|
|
24194
|
+
throw new Error(`invalid IPv6 address ${address}`);
|
|
24195
|
+
}
|
|
24196
|
+
const result = new Uint8Array(16);
|
|
24197
|
+
const view2 = new DataView(result.buffer);
|
|
24198
|
+
groups.forEach((group, index) => view2.setUint16(index * 2, Number.parseInt(group, 16)));
|
|
24199
|
+
return result;
|
|
24200
|
+
}
|
|
24201
|
+
function parseBcProvenanceV2Address(address) {
|
|
24202
|
+
if (address.includes(":"))
|
|
24203
|
+
return parseIpv6(address);
|
|
24204
|
+
return parseIpv4(address);
|
|
24205
|
+
}
|
|
24206
|
+
function endpointBytes(endpoint) {
|
|
24207
|
+
if (!validPort(endpoint.port) || !validPort(endpoint.manifestPort) || !validU32(endpoint.streamId)) {
|
|
24208
|
+
throw new Error("invalid BCPV2 endpoint");
|
|
24209
|
+
}
|
|
24210
|
+
const source = parseBcProvenanceV2Address(endpoint.sourceAddress);
|
|
24211
|
+
const group = parseBcProvenanceV2Address(endpoint.groupAddress);
|
|
24212
|
+
if (source.byteLength !== group.byteLength)
|
|
24213
|
+
throw new Error("endpoint address families differ");
|
|
24214
|
+
return { source, group };
|
|
24215
|
+
}
|
|
24216
|
+
function readU32(view2, offset) {
|
|
24217
|
+
return view2.getUint32(offset);
|
|
24218
|
+
}
|
|
24219
|
+
function readU642(view2, offset) {
|
|
24220
|
+
return view2.getBigUint64(offset);
|
|
24221
|
+
}
|
|
24222
|
+
function decodeBcProvenanceV2Record(input) {
|
|
24223
|
+
const bytes = Uint8Array.from(input);
|
|
24224
|
+
if (bytes.byteLength < BCPV2_MIN_RECORD_LENGTH || bytes.byteLength > BCPV2_MAX_RECORD_LENGTH || (bytes.byteLength - 132) % 32 !== 0) {
|
|
24225
|
+
throw new Error("invalid BCPV2 record length");
|
|
24226
|
+
}
|
|
24227
|
+
const view2 = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
24228
|
+
if (!equalBytes(bytes.subarray(0, 4), MAGIC))
|
|
24229
|
+
throw new Error("invalid BCPV2 record magic");
|
|
24230
|
+
const count = view2.getUint16(28);
|
|
24231
|
+
if (count < 1 || count > BCPV2_MAX_DIGESTS || bytes.byteLength !== 132 + count * 32) {
|
|
24232
|
+
throw new Error("invalid BCPV2 record count");
|
|
24233
|
+
}
|
|
24234
|
+
const body = bytes.slice(0, bytes.byteLength - BCPV2_SIGNATURE_LENGTH);
|
|
24235
|
+
const digests = [];
|
|
24236
|
+
for (let index = 0; index < count; index++) {
|
|
24237
|
+
digests.push(bytes.slice(BCPV2_HEADER_LENGTH + index * 32, BCPV2_HEADER_LENGTH + (index + 1) * 32));
|
|
24238
|
+
}
|
|
24239
|
+
return {
|
|
24240
|
+
kid: readU32(view2, 4),
|
|
24241
|
+
epoch: readU32(view2, 8),
|
|
24242
|
+
mseq: readU642(view2, 12),
|
|
24243
|
+
firstSeq: readU642(view2, 20),
|
|
24244
|
+
count,
|
|
24245
|
+
hashAlg: bytes[30],
|
|
24246
|
+
flags: bytes[31],
|
|
24247
|
+
sent: readU32(view2, 32),
|
|
24248
|
+
ctx: bytes.slice(36, 68),
|
|
24249
|
+
digests,
|
|
24250
|
+
signature: bytes.slice(bytes.byteLength - BCPV2_SIGNATURE_LENGTH),
|
|
24251
|
+
body,
|
|
24252
|
+
raw: bytes
|
|
24253
|
+
};
|
|
24254
|
+
}
|
|
24255
|
+
function bcProvenanceV2SignedMessage(record, endpoint) {
|
|
24256
|
+
const { source, group } = endpointBytes(endpoint);
|
|
24257
|
+
const prefix = new Uint8Array(SIGNING_DOMAIN.byteLength + 4 + source.byteLength + group.byteLength + 2);
|
|
24258
|
+
let offset = 0;
|
|
24259
|
+
prefix.set(SIGNING_DOMAIN, offset);
|
|
24260
|
+
offset += SIGNING_DOMAIN.byteLength;
|
|
24261
|
+
prefix.set(u322(endpoint.streamId), offset);
|
|
24262
|
+
offset += 4;
|
|
24263
|
+
prefix.set(source, offset);
|
|
24264
|
+
offset += source.byteLength;
|
|
24265
|
+
prefix.set(group, offset);
|
|
24266
|
+
offset += group.byteLength;
|
|
24267
|
+
prefix.set(u16(endpoint.manifestPort), offset);
|
|
24268
|
+
const message = new Uint8Array(prefix.byteLength + record.body.byteLength);
|
|
24269
|
+
message.set(prefix);
|
|
24270
|
+
message.set(record.body, prefix.byteLength);
|
|
24271
|
+
return message;
|
|
24272
|
+
}
|
|
24273
|
+
function bcProvenanceV2DatagramDigest(endpoint, payload) {
|
|
24274
|
+
if (payload.byteLength > 65535)
|
|
24275
|
+
throw new Error("datagram payload exceeds u16 length");
|
|
24276
|
+
const { source, group } = endpointBytes(endpoint);
|
|
24277
|
+
const pseudoheader = new Uint8Array(source.byteLength + group.byteLength + 2 + 2 + 2 + 2 + 4);
|
|
24278
|
+
let offset = 0;
|
|
24279
|
+
pseudoheader.set(source, offset);
|
|
24280
|
+
offset += source.byteLength;
|
|
24281
|
+
pseudoheader.set(group, offset);
|
|
24282
|
+
offset += group.byteLength;
|
|
24283
|
+
pseudoheader.set([0, 17], offset);
|
|
24284
|
+
offset += 2;
|
|
24285
|
+
pseudoheader.set(u16(payload.byteLength), offset);
|
|
24286
|
+
offset += 2;
|
|
24287
|
+
pseudoheader.set(u16(endpoint.port), offset);
|
|
24288
|
+
offset += 2;
|
|
24289
|
+
pseudoheader.set(u16(endpoint.port), offset);
|
|
24290
|
+
offset += 2;
|
|
24291
|
+
pseudoheader.set(u322(endpoint.streamId), offset);
|
|
24292
|
+
const input = new Uint8Array(pseudoheader.byteLength + payload.byteLength);
|
|
24293
|
+
input.set(pseudoheader);
|
|
24294
|
+
input.set(payload, pseudoheader.byteLength);
|
|
24295
|
+
return sha2562(input);
|
|
24296
|
+
}
|
|
24297
|
+
|
|
24298
|
+
// ../mmt-manifest-verify/node_modules/@noble/curves/esm/utils.js
|
|
24299
|
+
var _0n = /* @__PURE__ */ BigInt(0);
|
|
24300
|
+
var _1n = /* @__PURE__ */ BigInt(1);
|
|
24301
|
+
function _abool2(value, title = "") {
|
|
24302
|
+
if (typeof value !== "boolean") {
|
|
24303
|
+
const prefix = title && `"${title}"`;
|
|
24304
|
+
throw new Error(prefix + "expected boolean, got type=" + typeof value);
|
|
24305
|
+
}
|
|
24306
|
+
return value;
|
|
24307
|
+
}
|
|
24308
|
+
function _abytes2(value, length, title = "") {
|
|
24309
|
+
const bytes = isBytes(value);
|
|
24310
|
+
const len = value?.length;
|
|
24311
|
+
const needsLen = length !== void 0;
|
|
24312
|
+
if (!bytes || needsLen && len !== length) {
|
|
24313
|
+
const prefix = title && `"${title}" `;
|
|
24314
|
+
const ofLen = needsLen ? ` of length ${length}` : "";
|
|
24315
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
24316
|
+
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|
24317
|
+
}
|
|
24318
|
+
return value;
|
|
24319
|
+
}
|
|
24320
|
+
function hexToNumber(hex3) {
|
|
24321
|
+
if (typeof hex3 !== "string")
|
|
24322
|
+
throw new Error("hex string expected, got " + typeof hex3);
|
|
24323
|
+
return hex3 === "" ? _0n : BigInt("0x" + hex3);
|
|
24324
|
+
}
|
|
24325
|
+
function bytesToNumberBE(bytes) {
|
|
24326
|
+
return hexToNumber(bytesToHex(bytes));
|
|
24327
|
+
}
|
|
24328
|
+
function bytesToNumberLE(bytes) {
|
|
24329
|
+
abytes(bytes);
|
|
24330
|
+
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
|
24331
|
+
}
|
|
24332
|
+
function numberToBytesBE(n, len) {
|
|
24333
|
+
return hexToBytes(n.toString(16).padStart(len * 2, "0"));
|
|
24334
|
+
}
|
|
24335
|
+
function numberToBytesLE(n, len) {
|
|
24336
|
+
return numberToBytesBE(n, len).reverse();
|
|
24337
|
+
}
|
|
24338
|
+
function ensureBytes(title, hex3, expectedLength) {
|
|
24339
|
+
let res;
|
|
24340
|
+
if (typeof hex3 === "string") {
|
|
24341
|
+
try {
|
|
24342
|
+
res = hexToBytes(hex3);
|
|
24343
|
+
} catch (e) {
|
|
24344
|
+
throw new Error(title + " must be hex string or Uint8Array, cause: " + e);
|
|
24345
|
+
}
|
|
24346
|
+
} else if (isBytes(hex3)) {
|
|
24347
|
+
res = Uint8Array.from(hex3);
|
|
24348
|
+
} else {
|
|
24349
|
+
throw new Error(title + " must be hex string or Uint8Array");
|
|
24350
|
+
}
|
|
24351
|
+
const len = res.length;
|
|
24352
|
+
if (typeof expectedLength === "number" && len !== expectedLength)
|
|
24353
|
+
throw new Error(title + " of length " + expectedLength + " expected, got " + len);
|
|
24354
|
+
return res;
|
|
24355
|
+
}
|
|
24356
|
+
function equalBytes2(a, b) {
|
|
24357
|
+
if (a.length !== b.length)
|
|
24358
|
+
return false;
|
|
24359
|
+
let diff = 0;
|
|
24360
|
+
for (let i = 0; i < a.length; i++)
|
|
24361
|
+
diff |= a[i] ^ b[i];
|
|
24362
|
+
return diff === 0;
|
|
24363
|
+
}
|
|
24364
|
+
function copyBytes(bytes) {
|
|
24365
|
+
return Uint8Array.from(bytes);
|
|
24366
|
+
}
|
|
24367
|
+
var isPosBig = (n) => typeof n === "bigint" && _0n <= n;
|
|
24368
|
+
function inRange(n, min, max) {
|
|
24369
|
+
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
|
|
24370
|
+
}
|
|
24371
|
+
function aInRange(title, n, min, max) {
|
|
24372
|
+
if (!inRange(n, min, max))
|
|
24373
|
+
throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
|
|
24374
|
+
}
|
|
24375
|
+
function bitLen(n) {
|
|
24376
|
+
let len;
|
|
24377
|
+
for (len = 0; n > _0n; n >>= _1n, len += 1)
|
|
24378
|
+
;
|
|
24379
|
+
return len;
|
|
24380
|
+
}
|
|
24381
|
+
var bitMask = (n) => (_1n << BigInt(n)) - _1n;
|
|
24382
|
+
function _validateObject(object2, fields, optFields = {}) {
|
|
24383
|
+
if (!object2 || typeof object2 !== "object")
|
|
24384
|
+
throw new Error("expected valid options object");
|
|
24385
|
+
function checkField2(fieldName, expectedType, isOpt) {
|
|
24386
|
+
const val = object2[fieldName];
|
|
24387
|
+
if (isOpt && val === void 0)
|
|
24388
|
+
return;
|
|
24389
|
+
const current = typeof val;
|
|
24390
|
+
if (current !== expectedType || val === null)
|
|
24391
|
+
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
|
|
24392
|
+
}
|
|
24393
|
+
Object.entries(fields).forEach(([k, v]) => checkField2(k, v, false));
|
|
24394
|
+
Object.entries(optFields).forEach(([k, v]) => checkField2(k, v, true));
|
|
24395
|
+
}
|
|
24396
|
+
var notImplemented = () => {
|
|
24397
|
+
throw new Error("not implemented");
|
|
24398
|
+
};
|
|
24399
|
+
function memoized(fn) {
|
|
24400
|
+
const map = /* @__PURE__ */ new WeakMap();
|
|
24401
|
+
return (arg, ...args) => {
|
|
24402
|
+
const val = map.get(arg);
|
|
24403
|
+
if (val !== void 0)
|
|
24404
|
+
return val;
|
|
24405
|
+
const computed = fn(arg, ...args);
|
|
24406
|
+
map.set(arg, computed);
|
|
24407
|
+
return computed;
|
|
24408
|
+
};
|
|
24409
|
+
}
|
|
24410
|
+
|
|
24411
|
+
// ../mmt-manifest-verify/node_modules/@noble/curves/esm/abstract/modular.js
|
|
24412
|
+
var _0n2 = BigInt(0);
|
|
24413
|
+
var _1n2 = BigInt(1);
|
|
24414
|
+
var _2n = /* @__PURE__ */ BigInt(2);
|
|
24415
|
+
var _3n = /* @__PURE__ */ BigInt(3);
|
|
24416
|
+
var _4n = /* @__PURE__ */ BigInt(4);
|
|
24417
|
+
var _5n = /* @__PURE__ */ BigInt(5);
|
|
24418
|
+
var _7n = /* @__PURE__ */ BigInt(7);
|
|
24419
|
+
var _8n = /* @__PURE__ */ BigInt(8);
|
|
24420
|
+
var _9n = /* @__PURE__ */ BigInt(9);
|
|
24421
|
+
var _16n = /* @__PURE__ */ BigInt(16);
|
|
24422
|
+
function mod(a, b) {
|
|
24423
|
+
const result = a % b;
|
|
24424
|
+
return result >= _0n2 ? result : b + result;
|
|
24425
|
+
}
|
|
24426
|
+
function pow2(x, power, modulo) {
|
|
24427
|
+
let res = x;
|
|
24428
|
+
while (power-- > _0n2) {
|
|
24429
|
+
res *= res;
|
|
24430
|
+
res %= modulo;
|
|
24431
|
+
}
|
|
24432
|
+
return res;
|
|
24433
|
+
}
|
|
24434
|
+
function invert(number, modulo) {
|
|
24435
|
+
if (number === _0n2)
|
|
24436
|
+
throw new Error("invert: expected non-zero number");
|
|
24437
|
+
if (modulo <= _0n2)
|
|
24438
|
+
throw new Error("invert: expected positive modulus, got " + modulo);
|
|
24439
|
+
let a = mod(number, modulo);
|
|
24440
|
+
let b = modulo;
|
|
24441
|
+
let x = _0n2, y = _1n2, u = _1n2, v = _0n2;
|
|
24442
|
+
while (a !== _0n2) {
|
|
24443
|
+
const q = b / a;
|
|
24444
|
+
const r = b % a;
|
|
24445
|
+
const m = x - u * q;
|
|
24446
|
+
const n = y - v * q;
|
|
24447
|
+
b = a, a = r, x = u, y = v, u = m, v = n;
|
|
24448
|
+
}
|
|
24449
|
+
const gcd = b;
|
|
24450
|
+
if (gcd !== _1n2)
|
|
24451
|
+
throw new Error("invert: does not exist");
|
|
24452
|
+
return mod(x, modulo);
|
|
24453
|
+
}
|
|
24454
|
+
function assertIsSquare(Fp2, root, n) {
|
|
24455
|
+
if (!Fp2.eql(Fp2.sqr(root), n))
|
|
24456
|
+
throw new Error("Cannot find square root");
|
|
24457
|
+
}
|
|
24458
|
+
function sqrt3mod4(Fp2, n) {
|
|
24459
|
+
const p1div4 = (Fp2.ORDER + _1n2) / _4n;
|
|
24460
|
+
const root = Fp2.pow(n, p1div4);
|
|
24461
|
+
assertIsSquare(Fp2, root, n);
|
|
24462
|
+
return root;
|
|
24463
|
+
}
|
|
24464
|
+
function sqrt5mod8(Fp2, n) {
|
|
24465
|
+
const p5div8 = (Fp2.ORDER - _5n) / _8n;
|
|
24466
|
+
const n2 = Fp2.mul(n, _2n);
|
|
24467
|
+
const v = Fp2.pow(n2, p5div8);
|
|
24468
|
+
const nv = Fp2.mul(n, v);
|
|
24469
|
+
const i = Fp2.mul(Fp2.mul(nv, _2n), v);
|
|
24470
|
+
const root = Fp2.mul(nv, Fp2.sub(i, Fp2.ONE));
|
|
24471
|
+
assertIsSquare(Fp2, root, n);
|
|
24472
|
+
return root;
|
|
24473
|
+
}
|
|
24474
|
+
function sqrt9mod16(P) {
|
|
24475
|
+
const Fp_ = Field(P);
|
|
24476
|
+
const tn = tonelliShanks(P);
|
|
24477
|
+
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));
|
|
24478
|
+
const c2 = tn(Fp_, c1);
|
|
24479
|
+
const c3 = tn(Fp_, Fp_.neg(c1));
|
|
24480
|
+
const c4 = (P + _7n) / _16n;
|
|
24481
|
+
return (Fp2, n) => {
|
|
24482
|
+
let tv1 = Fp2.pow(n, c4);
|
|
24483
|
+
let tv2 = Fp2.mul(tv1, c1);
|
|
24484
|
+
const tv3 = Fp2.mul(tv1, c2);
|
|
24485
|
+
const tv4 = Fp2.mul(tv1, c3);
|
|
24486
|
+
const e1 = Fp2.eql(Fp2.sqr(tv2), n);
|
|
24487
|
+
const e2 = Fp2.eql(Fp2.sqr(tv3), n);
|
|
24488
|
+
tv1 = Fp2.cmov(tv1, tv2, e1);
|
|
24489
|
+
tv2 = Fp2.cmov(tv4, tv3, e2);
|
|
24490
|
+
const e3 = Fp2.eql(Fp2.sqr(tv2), n);
|
|
24491
|
+
const root = Fp2.cmov(tv1, tv2, e3);
|
|
24492
|
+
assertIsSquare(Fp2, root, n);
|
|
24493
|
+
return root;
|
|
24494
|
+
};
|
|
24495
|
+
}
|
|
24496
|
+
function tonelliShanks(P) {
|
|
24497
|
+
if (P < _3n)
|
|
24498
|
+
throw new Error("sqrt is not defined for small field");
|
|
24499
|
+
let Q = P - _1n2;
|
|
24500
|
+
let S = 0;
|
|
24501
|
+
while (Q % _2n === _0n2) {
|
|
24502
|
+
Q /= _2n;
|
|
24503
|
+
S++;
|
|
24504
|
+
}
|
|
24505
|
+
let Z = _2n;
|
|
24506
|
+
const _Fp = Field(P);
|
|
24507
|
+
while (FpLegendre(_Fp, Z) === 1) {
|
|
24508
|
+
if (Z++ > 1e3)
|
|
24509
|
+
throw new Error("Cannot find square root: probably non-prime P");
|
|
24510
|
+
}
|
|
24511
|
+
if (S === 1)
|
|
24512
|
+
return sqrt3mod4;
|
|
24513
|
+
let cc = _Fp.pow(Z, Q);
|
|
24514
|
+
const Q1div2 = (Q + _1n2) / _2n;
|
|
24515
|
+
return function tonelliSlow(Fp2, n) {
|
|
24516
|
+
if (Fp2.is0(n))
|
|
24517
|
+
return n;
|
|
24518
|
+
if (FpLegendre(Fp2, n) !== 1)
|
|
24519
|
+
throw new Error("Cannot find square root");
|
|
24520
|
+
let M = S;
|
|
24521
|
+
let c = Fp2.mul(Fp2.ONE, cc);
|
|
24522
|
+
let t = Fp2.pow(n, Q);
|
|
24523
|
+
let R = Fp2.pow(n, Q1div2);
|
|
24524
|
+
while (!Fp2.eql(t, Fp2.ONE)) {
|
|
24525
|
+
if (Fp2.is0(t))
|
|
24526
|
+
return Fp2.ZERO;
|
|
24527
|
+
let i = 1;
|
|
24528
|
+
let t_tmp = Fp2.sqr(t);
|
|
24529
|
+
while (!Fp2.eql(t_tmp, Fp2.ONE)) {
|
|
24530
|
+
i++;
|
|
24531
|
+
t_tmp = Fp2.sqr(t_tmp);
|
|
24532
|
+
if (i === M)
|
|
24533
|
+
throw new Error("Cannot find square root");
|
|
24534
|
+
}
|
|
24535
|
+
const exponent = _1n2 << BigInt(M - i - 1);
|
|
24536
|
+
const b = Fp2.pow(c, exponent);
|
|
24537
|
+
M = i;
|
|
24538
|
+
c = Fp2.sqr(b);
|
|
24539
|
+
t = Fp2.mul(t, c);
|
|
24540
|
+
R = Fp2.mul(R, b);
|
|
24541
|
+
}
|
|
24542
|
+
return R;
|
|
24543
|
+
};
|
|
24544
|
+
}
|
|
24545
|
+
function FpSqrt(P) {
|
|
24546
|
+
if (P % _4n === _3n)
|
|
24547
|
+
return sqrt3mod4;
|
|
24548
|
+
if (P % _8n === _5n)
|
|
24549
|
+
return sqrt5mod8;
|
|
24550
|
+
if (P % _16n === _9n)
|
|
24551
|
+
return sqrt9mod16(P);
|
|
24552
|
+
return tonelliShanks(P);
|
|
24553
|
+
}
|
|
24554
|
+
var isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n2) === _1n2;
|
|
24555
|
+
var FIELD_FIELDS = [
|
|
24556
|
+
"create",
|
|
24557
|
+
"isValid",
|
|
24558
|
+
"is0",
|
|
24559
|
+
"neg",
|
|
24560
|
+
"inv",
|
|
24561
|
+
"sqrt",
|
|
24562
|
+
"sqr",
|
|
24563
|
+
"eql",
|
|
24564
|
+
"add",
|
|
24565
|
+
"sub",
|
|
24566
|
+
"mul",
|
|
24567
|
+
"pow",
|
|
24568
|
+
"div",
|
|
24569
|
+
"addN",
|
|
24570
|
+
"subN",
|
|
24571
|
+
"mulN",
|
|
24572
|
+
"sqrN"
|
|
24573
|
+
];
|
|
24574
|
+
function validateField(field) {
|
|
24575
|
+
const initial = {
|
|
24576
|
+
ORDER: "bigint",
|
|
24577
|
+
MASK: "bigint",
|
|
24578
|
+
BYTES: "number",
|
|
24579
|
+
BITS: "number"
|
|
24580
|
+
};
|
|
24581
|
+
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|
24582
|
+
map[val] = "function";
|
|
24583
|
+
return map;
|
|
24584
|
+
}, initial);
|
|
24585
|
+
_validateObject(field, opts);
|
|
24586
|
+
return field;
|
|
24587
|
+
}
|
|
24588
|
+
function FpPow(Fp2, num, power) {
|
|
24589
|
+
if (power < _0n2)
|
|
24590
|
+
throw new Error("invalid exponent, negatives unsupported");
|
|
24591
|
+
if (power === _0n2)
|
|
24592
|
+
return Fp2.ONE;
|
|
24593
|
+
if (power === _1n2)
|
|
24594
|
+
return num;
|
|
24595
|
+
let p = Fp2.ONE;
|
|
24596
|
+
let d = num;
|
|
24597
|
+
while (power > _0n2) {
|
|
24598
|
+
if (power & _1n2)
|
|
24599
|
+
p = Fp2.mul(p, d);
|
|
24600
|
+
d = Fp2.sqr(d);
|
|
24601
|
+
power >>= _1n2;
|
|
24602
|
+
}
|
|
24603
|
+
return p;
|
|
24604
|
+
}
|
|
24605
|
+
function FpInvertBatch(Fp2, nums, passZero = false) {
|
|
24606
|
+
const inverted = new Array(nums.length).fill(passZero ? Fp2.ZERO : void 0);
|
|
24607
|
+
const multipliedAcc = nums.reduce((acc, num, i) => {
|
|
24608
|
+
if (Fp2.is0(num))
|
|
24609
|
+
return acc;
|
|
24610
|
+
inverted[i] = acc;
|
|
24611
|
+
return Fp2.mul(acc, num);
|
|
24612
|
+
}, Fp2.ONE);
|
|
24613
|
+
const invertedAcc = Fp2.inv(multipliedAcc);
|
|
24614
|
+
nums.reduceRight((acc, num, i) => {
|
|
24615
|
+
if (Fp2.is0(num))
|
|
24616
|
+
return acc;
|
|
24617
|
+
inverted[i] = Fp2.mul(acc, inverted[i]);
|
|
24618
|
+
return Fp2.mul(acc, num);
|
|
24619
|
+
}, invertedAcc);
|
|
24620
|
+
return inverted;
|
|
24621
|
+
}
|
|
24622
|
+
function FpLegendre(Fp2, n) {
|
|
24623
|
+
const p1mod2 = (Fp2.ORDER - _1n2) / _2n;
|
|
24624
|
+
const powered = Fp2.pow(n, p1mod2);
|
|
24625
|
+
const yes = Fp2.eql(powered, Fp2.ONE);
|
|
24626
|
+
const zero = Fp2.eql(powered, Fp2.ZERO);
|
|
24627
|
+
const no = Fp2.eql(powered, Fp2.neg(Fp2.ONE));
|
|
24628
|
+
if (!yes && !zero && !no)
|
|
24629
|
+
throw new Error("invalid Legendre symbol result");
|
|
24630
|
+
return yes ? 1 : zero ? 0 : -1;
|
|
24631
|
+
}
|
|
24632
|
+
function nLength(n, nBitLength) {
|
|
24633
|
+
if (nBitLength !== void 0)
|
|
24634
|
+
anumber(nBitLength);
|
|
24635
|
+
const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length;
|
|
24636
|
+
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
24637
|
+
return { nBitLength: _nBitLength, nByteLength };
|
|
24638
|
+
}
|
|
24639
|
+
function Field(ORDER, bitLenOrOpts, isLE = false, opts = {}) {
|
|
24640
|
+
if (ORDER <= _0n2)
|
|
24641
|
+
throw new Error("invalid field: expected ORDER > 0, got " + ORDER);
|
|
24642
|
+
let _nbitLength = void 0;
|
|
24643
|
+
let _sqrt = void 0;
|
|
24644
|
+
let modFromBytes = false;
|
|
24645
|
+
let allowedLengths = void 0;
|
|
24646
|
+
if (typeof bitLenOrOpts === "object" && bitLenOrOpts != null) {
|
|
24647
|
+
if (opts.sqrt || isLE)
|
|
24648
|
+
throw new Error("cannot specify opts in two arguments");
|
|
24649
|
+
const _opts = bitLenOrOpts;
|
|
24650
|
+
if (_opts.BITS)
|
|
24651
|
+
_nbitLength = _opts.BITS;
|
|
24652
|
+
if (_opts.sqrt)
|
|
24653
|
+
_sqrt = _opts.sqrt;
|
|
24654
|
+
if (typeof _opts.isLE === "boolean")
|
|
24655
|
+
isLE = _opts.isLE;
|
|
24656
|
+
if (typeof _opts.modFromBytes === "boolean")
|
|
24657
|
+
modFromBytes = _opts.modFromBytes;
|
|
24658
|
+
allowedLengths = _opts.allowedLengths;
|
|
24659
|
+
} else {
|
|
24660
|
+
if (typeof bitLenOrOpts === "number")
|
|
24661
|
+
_nbitLength = bitLenOrOpts;
|
|
24662
|
+
if (opts.sqrt)
|
|
24663
|
+
_sqrt = opts.sqrt;
|
|
24664
|
+
}
|
|
24665
|
+
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
|
|
24666
|
+
if (BYTES > 2048)
|
|
24667
|
+
throw new Error("invalid field: expected ORDER of <= 2048 bytes");
|
|
24668
|
+
let sqrtP;
|
|
24669
|
+
const f = Object.freeze({
|
|
24670
|
+
ORDER,
|
|
24671
|
+
isLE,
|
|
24672
|
+
BITS,
|
|
24673
|
+
BYTES,
|
|
24674
|
+
MASK: bitMask(BITS),
|
|
24675
|
+
ZERO: _0n2,
|
|
24676
|
+
ONE: _1n2,
|
|
24677
|
+
allowedLengths,
|
|
24678
|
+
create: (num) => mod(num, ORDER),
|
|
24679
|
+
isValid: (num) => {
|
|
24680
|
+
if (typeof num !== "bigint")
|
|
24681
|
+
throw new Error("invalid field element: expected bigint, got " + typeof num);
|
|
24682
|
+
return _0n2 <= num && num < ORDER;
|
|
24683
|
+
},
|
|
24684
|
+
is0: (num) => num === _0n2,
|
|
24685
|
+
// is valid and invertible
|
|
24686
|
+
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
|
|
24687
|
+
isOdd: (num) => (num & _1n2) === _1n2,
|
|
24688
|
+
neg: (num) => mod(-num, ORDER),
|
|
24689
|
+
eql: (lhs, rhs) => lhs === rhs,
|
|
24690
|
+
sqr: (num) => mod(num * num, ORDER),
|
|
24691
|
+
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
|
24692
|
+
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
|
24693
|
+
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
|
24694
|
+
pow: (num, power) => FpPow(f, num, power),
|
|
24695
|
+
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
|
24696
|
+
// Same as above, but doesn't normalize
|
|
24697
|
+
sqrN: (num) => num * num,
|
|
24698
|
+
addN: (lhs, rhs) => lhs + rhs,
|
|
24699
|
+
subN: (lhs, rhs) => lhs - rhs,
|
|
24700
|
+
mulN: (lhs, rhs) => lhs * rhs,
|
|
24701
|
+
inv: (num) => invert(num, ORDER),
|
|
24702
|
+
sqrt: _sqrt || ((n) => {
|
|
24703
|
+
if (!sqrtP)
|
|
24704
|
+
sqrtP = FpSqrt(ORDER);
|
|
24705
|
+
return sqrtP(f, n);
|
|
24706
|
+
}),
|
|
24707
|
+
toBytes: (num) => isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES),
|
|
24708
|
+
fromBytes: (bytes, skipValidation = true) => {
|
|
24709
|
+
if (allowedLengths) {
|
|
24710
|
+
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
24711
|
+
throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes.length);
|
|
24712
|
+
}
|
|
24713
|
+
const padded = new Uint8Array(BYTES);
|
|
24714
|
+
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
24715
|
+
bytes = padded;
|
|
24716
|
+
}
|
|
24717
|
+
if (bytes.length !== BYTES)
|
|
24718
|
+
throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes.length);
|
|
24719
|
+
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
24720
|
+
if (modFromBytes)
|
|
24721
|
+
scalar = mod(scalar, ORDER);
|
|
24722
|
+
if (!skipValidation) {
|
|
24723
|
+
if (!f.isValid(scalar))
|
|
24724
|
+
throw new Error("invalid field element: outside of range 0..ORDER");
|
|
24725
|
+
}
|
|
24726
|
+
return scalar;
|
|
24727
|
+
},
|
|
24728
|
+
// TODO: we don't need it here, move out to separate fn
|
|
24729
|
+
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
24730
|
+
// We can't move this out because Fp6, Fp12 implement it
|
|
24731
|
+
// and it's unclear what to return in there.
|
|
24732
|
+
cmov: (a, b, c) => c ? b : a
|
|
24733
|
+
});
|
|
24734
|
+
return Object.freeze(f);
|
|
24735
|
+
}
|
|
24736
|
+
|
|
24737
|
+
// ../mmt-manifest-verify/node_modules/@noble/curves/esm/abstract/curve.js
|
|
24738
|
+
var _0n3 = BigInt(0);
|
|
24739
|
+
var _1n3 = BigInt(1);
|
|
24740
|
+
function negateCt(condition, item) {
|
|
24741
|
+
const neg = item.negate();
|
|
24742
|
+
return condition ? neg : item;
|
|
24743
|
+
}
|
|
24744
|
+
function normalizeZ(c, points) {
|
|
24745
|
+
const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
|
|
24746
|
+
return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));
|
|
24747
|
+
}
|
|
24748
|
+
function validateW(W, bits) {
|
|
24749
|
+
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
|
24750
|
+
throw new Error("invalid window size, expected [1.." + bits + "], got W=" + W);
|
|
24751
|
+
}
|
|
24752
|
+
function calcWOpts(W, scalarBits) {
|
|
24753
|
+
validateW(W, scalarBits);
|
|
24754
|
+
const windows = Math.ceil(scalarBits / W) + 1;
|
|
24755
|
+
const windowSize = 2 ** (W - 1);
|
|
24756
|
+
const maxNumber = 2 ** W;
|
|
24757
|
+
const mask = bitMask(W);
|
|
24758
|
+
const shiftBy = BigInt(W);
|
|
24759
|
+
return { windows, windowSize, mask, maxNumber, shiftBy };
|
|
24760
|
+
}
|
|
24761
|
+
function calcOffsets(n, window2, wOpts) {
|
|
24762
|
+
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
|
|
24763
|
+
let wbits = Number(n & mask);
|
|
24764
|
+
let nextN = n >> shiftBy;
|
|
24765
|
+
if (wbits > windowSize) {
|
|
24766
|
+
wbits -= maxNumber;
|
|
24767
|
+
nextN += _1n3;
|
|
24768
|
+
}
|
|
24769
|
+
const offsetStart = window2 * windowSize;
|
|
24770
|
+
const offset = offsetStart + Math.abs(wbits) - 1;
|
|
24771
|
+
const isZero = wbits === 0;
|
|
24772
|
+
const isNeg = wbits < 0;
|
|
24773
|
+
const isNegF = window2 % 2 !== 0;
|
|
24774
|
+
const offsetF = offsetStart;
|
|
24775
|
+
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
|
|
24776
|
+
}
|
|
24777
|
+
function validateMSMPoints(points, c) {
|
|
24778
|
+
if (!Array.isArray(points))
|
|
24779
|
+
throw new Error("array expected");
|
|
24780
|
+
points.forEach((p, i) => {
|
|
24781
|
+
if (!(p instanceof c))
|
|
24782
|
+
throw new Error("invalid point at index " + i);
|
|
24783
|
+
});
|
|
24784
|
+
}
|
|
24785
|
+
function validateMSMScalars(scalars, field) {
|
|
24786
|
+
if (!Array.isArray(scalars))
|
|
24787
|
+
throw new Error("array of scalars expected");
|
|
24788
|
+
scalars.forEach((s, i) => {
|
|
24789
|
+
if (!field.isValid(s))
|
|
24790
|
+
throw new Error("invalid scalar at index " + i);
|
|
24791
|
+
});
|
|
24792
|
+
}
|
|
24793
|
+
var pointPrecomputes = /* @__PURE__ */ new WeakMap();
|
|
24794
|
+
var pointWindowSizes = /* @__PURE__ */ new WeakMap();
|
|
24795
|
+
function getW(P) {
|
|
24796
|
+
return pointWindowSizes.get(P) || 1;
|
|
24797
|
+
}
|
|
24798
|
+
function assert0(n) {
|
|
24799
|
+
if (n !== _0n3)
|
|
24800
|
+
throw new Error("invalid wNAF");
|
|
24801
|
+
}
|
|
24802
|
+
var wNAF = class {
|
|
24803
|
+
// Parametrized with a given Point class (not individual point)
|
|
24804
|
+
constructor(Point, bits) {
|
|
24805
|
+
this.BASE = Point.BASE;
|
|
24806
|
+
this.ZERO = Point.ZERO;
|
|
24807
|
+
this.Fn = Point.Fn;
|
|
24808
|
+
this.bits = bits;
|
|
24809
|
+
}
|
|
24810
|
+
// non-const time multiplication ladder
|
|
24811
|
+
_unsafeLadder(elm, n, p = this.ZERO) {
|
|
24812
|
+
let d = elm;
|
|
24813
|
+
while (n > _0n3) {
|
|
24814
|
+
if (n & _1n3)
|
|
24815
|
+
p = p.add(d);
|
|
24816
|
+
d = d.double();
|
|
24817
|
+
n >>= _1n3;
|
|
24818
|
+
}
|
|
24819
|
+
return p;
|
|
24820
|
+
}
|
|
24821
|
+
/**
|
|
24822
|
+
* Creates a wNAF precomputation window. Used for caching.
|
|
24823
|
+
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
24824
|
+
* Number of precomputed points depends on the curve size:
|
|
24825
|
+
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
|
24826
|
+
* - 𝑊 is the window size
|
|
24827
|
+
* - 𝑛 is the bitlength of the curve order.
|
|
24828
|
+
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
|
24829
|
+
* @param point Point instance
|
|
24830
|
+
* @param W window size
|
|
24831
|
+
* @returns precomputed point tables flattened to a single array
|
|
24832
|
+
*/
|
|
24833
|
+
precomputeWindow(point, W) {
|
|
24834
|
+
const { windows, windowSize } = calcWOpts(W, this.bits);
|
|
24835
|
+
const points = [];
|
|
24836
|
+
let p = point;
|
|
24837
|
+
let base = p;
|
|
24838
|
+
for (let window2 = 0; window2 < windows; window2++) {
|
|
24839
|
+
base = p;
|
|
24840
|
+
points.push(base);
|
|
24841
|
+
for (let i = 1; i < windowSize; i++) {
|
|
24842
|
+
base = base.add(p);
|
|
24843
|
+
points.push(base);
|
|
24844
|
+
}
|
|
24845
|
+
p = base.double();
|
|
24846
|
+
}
|
|
24847
|
+
return points;
|
|
24848
|
+
}
|
|
24849
|
+
/**
|
|
24850
|
+
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
|
24851
|
+
* More compact implementation:
|
|
24852
|
+
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
|
24853
|
+
* @returns real and fake (for const-time) points
|
|
24854
|
+
*/
|
|
24855
|
+
wNAF(W, precomputes, n) {
|
|
24856
|
+
if (!this.Fn.isValid(n))
|
|
24857
|
+
throw new Error("invalid scalar");
|
|
24858
|
+
let p = this.ZERO;
|
|
24859
|
+
let f = this.BASE;
|
|
24860
|
+
const wo = calcWOpts(W, this.bits);
|
|
24861
|
+
for (let window2 = 0; window2 < wo.windows; window2++) {
|
|
24862
|
+
const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window2, wo);
|
|
24863
|
+
n = nextN;
|
|
24864
|
+
if (isZero) {
|
|
24865
|
+
f = f.add(negateCt(isNegF, precomputes[offsetF]));
|
|
24866
|
+
} else {
|
|
24867
|
+
p = p.add(negateCt(isNeg, precomputes[offset]));
|
|
24868
|
+
}
|
|
24869
|
+
}
|
|
24870
|
+
assert0(n);
|
|
24871
|
+
return { p, f };
|
|
24872
|
+
}
|
|
24873
|
+
/**
|
|
24874
|
+
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
|
24875
|
+
* @param acc accumulator point to add result of multiplication
|
|
24876
|
+
* @returns point
|
|
24877
|
+
*/
|
|
24878
|
+
wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
|
|
24879
|
+
const wo = calcWOpts(W, this.bits);
|
|
24880
|
+
for (let window2 = 0; window2 < wo.windows; window2++) {
|
|
24881
|
+
if (n === _0n3)
|
|
24882
|
+
break;
|
|
24883
|
+
const { nextN, offset, isZero, isNeg } = calcOffsets(n, window2, wo);
|
|
24884
|
+
n = nextN;
|
|
24885
|
+
if (isZero) {
|
|
24886
|
+
continue;
|
|
24887
|
+
} else {
|
|
24888
|
+
const item = precomputes[offset];
|
|
24889
|
+
acc = acc.add(isNeg ? item.negate() : item);
|
|
24890
|
+
}
|
|
24891
|
+
}
|
|
24892
|
+
assert0(n);
|
|
24893
|
+
return acc;
|
|
24894
|
+
}
|
|
24895
|
+
getPrecomputes(W, point, transform) {
|
|
24896
|
+
let comp = pointPrecomputes.get(point);
|
|
24897
|
+
if (!comp) {
|
|
24898
|
+
comp = this.precomputeWindow(point, W);
|
|
24899
|
+
if (W !== 1) {
|
|
24900
|
+
if (typeof transform === "function")
|
|
24901
|
+
comp = transform(comp);
|
|
24902
|
+
pointPrecomputes.set(point, comp);
|
|
24903
|
+
}
|
|
24904
|
+
}
|
|
24905
|
+
return comp;
|
|
24906
|
+
}
|
|
24907
|
+
cached(point, scalar, transform) {
|
|
24908
|
+
const W = getW(point);
|
|
24909
|
+
return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
|
|
24910
|
+
}
|
|
24911
|
+
unsafe(point, scalar, transform, prev) {
|
|
24912
|
+
const W = getW(point);
|
|
24913
|
+
if (W === 1)
|
|
24914
|
+
return this._unsafeLadder(point, scalar, prev);
|
|
24915
|
+
return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
|
|
24916
|
+
}
|
|
24917
|
+
// We calculate precomputes for elliptic curve point multiplication
|
|
24918
|
+
// using windowed method. This specifies window size and
|
|
24919
|
+
// stores precomputed values. Usually only base point would be precomputed.
|
|
24920
|
+
createCache(P, W) {
|
|
24921
|
+
validateW(W, this.bits);
|
|
24922
|
+
pointWindowSizes.set(P, W);
|
|
24923
|
+
pointPrecomputes.delete(P);
|
|
24924
|
+
}
|
|
24925
|
+
hasCache(elm) {
|
|
24926
|
+
return getW(elm) !== 1;
|
|
24927
|
+
}
|
|
24928
|
+
};
|
|
24929
|
+
function pippenger(c, fieldN, points, scalars) {
|
|
24930
|
+
validateMSMPoints(points, c);
|
|
24931
|
+
validateMSMScalars(scalars, fieldN);
|
|
24932
|
+
const plength = points.length;
|
|
24933
|
+
const slength = scalars.length;
|
|
24934
|
+
if (plength !== slength)
|
|
24935
|
+
throw new Error("arrays of points and scalars must have equal length");
|
|
24936
|
+
const zero = c.ZERO;
|
|
24937
|
+
const wbits = bitLen(BigInt(plength));
|
|
24938
|
+
let windowSize = 1;
|
|
24939
|
+
if (wbits > 12)
|
|
24940
|
+
windowSize = wbits - 3;
|
|
24941
|
+
else if (wbits > 4)
|
|
24942
|
+
windowSize = wbits - 2;
|
|
24943
|
+
else if (wbits > 0)
|
|
24944
|
+
windowSize = 2;
|
|
24945
|
+
const MASK = bitMask(windowSize);
|
|
24946
|
+
const buckets = new Array(Number(MASK) + 1).fill(zero);
|
|
24947
|
+
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
|
|
24948
|
+
let sum = zero;
|
|
24949
|
+
for (let i = lastBits; i >= 0; i -= windowSize) {
|
|
24950
|
+
buckets.fill(zero);
|
|
24951
|
+
for (let j = 0; j < slength; j++) {
|
|
24952
|
+
const scalar = scalars[j];
|
|
24953
|
+
const wbits2 = Number(scalar >> BigInt(i) & MASK);
|
|
24954
|
+
buckets[wbits2] = buckets[wbits2].add(points[j]);
|
|
24955
|
+
}
|
|
24956
|
+
let resI = zero;
|
|
24957
|
+
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
|
|
24958
|
+
sumI = sumI.add(buckets[j]);
|
|
24959
|
+
resI = resI.add(sumI);
|
|
24960
|
+
}
|
|
24961
|
+
sum = sum.add(resI);
|
|
24962
|
+
if (i !== 0)
|
|
24963
|
+
for (let j = 0; j < windowSize; j++)
|
|
24964
|
+
sum = sum.double();
|
|
24965
|
+
}
|
|
24966
|
+
return sum;
|
|
24967
|
+
}
|
|
24968
|
+
function createField(order, field, isLE) {
|
|
24969
|
+
if (field) {
|
|
24970
|
+
if (field.ORDER !== order)
|
|
24971
|
+
throw new Error("Field.ORDER must match order: Fp == p, Fn == n");
|
|
24972
|
+
validateField(field);
|
|
24973
|
+
return field;
|
|
24974
|
+
} else {
|
|
24975
|
+
return Field(order, { isLE });
|
|
24976
|
+
}
|
|
24977
|
+
}
|
|
24978
|
+
function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
|
24979
|
+
if (FpFnLE === void 0)
|
|
24980
|
+
FpFnLE = type === "edwards";
|
|
24981
|
+
if (!CURVE || typeof CURVE !== "object")
|
|
24982
|
+
throw new Error(`expected valid ${type} CURVE object`);
|
|
24983
|
+
for (const p of ["p", "n", "h"]) {
|
|
24984
|
+
const val = CURVE[p];
|
|
24985
|
+
if (!(typeof val === "bigint" && val > _0n3))
|
|
24986
|
+
throw new Error(`CURVE.${p} must be positive bigint`);
|
|
24987
|
+
}
|
|
24988
|
+
const Fp2 = createField(CURVE.p, curveOpts.Fp, FpFnLE);
|
|
24989
|
+
const Fn2 = createField(CURVE.n, curveOpts.Fn, FpFnLE);
|
|
24990
|
+
const _b = type === "weierstrass" ? "b" : "d";
|
|
24991
|
+
const params = ["Gx", "Gy", "a", _b];
|
|
24992
|
+
for (const p of params) {
|
|
24993
|
+
if (!Fp2.isValid(CURVE[p]))
|
|
24994
|
+
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
|
|
24995
|
+
}
|
|
24996
|
+
CURVE = Object.freeze(Object.assign({}, CURVE));
|
|
24997
|
+
return { CURVE, Fp: Fp2, Fn: Fn2 };
|
|
24998
|
+
}
|
|
24999
|
+
|
|
25000
|
+
// ../mmt-manifest-verify/node_modules/@noble/curves/esm/abstract/edwards.js
|
|
25001
|
+
var _0n4 = BigInt(0);
|
|
25002
|
+
var _1n4 = BigInt(1);
|
|
25003
|
+
var _2n2 = BigInt(2);
|
|
25004
|
+
var _8n2 = BigInt(8);
|
|
25005
|
+
function isEdValidXY(Fp2, CURVE, x, y) {
|
|
25006
|
+
const x2 = Fp2.sqr(x);
|
|
25007
|
+
const y2 = Fp2.sqr(y);
|
|
25008
|
+
const left = Fp2.add(Fp2.mul(CURVE.a, x2), y2);
|
|
25009
|
+
const right = Fp2.add(Fp2.ONE, Fp2.mul(CURVE.d, Fp2.mul(x2, y2)));
|
|
25010
|
+
return Fp2.eql(left, right);
|
|
25011
|
+
}
|
|
25012
|
+
function edwards(params, extraOpts = {}) {
|
|
25013
|
+
const validated = _createCurveFields("edwards", params, extraOpts, extraOpts.FpFnLE);
|
|
25014
|
+
const { Fp: Fp2, Fn: Fn2 } = validated;
|
|
25015
|
+
let CURVE = validated.CURVE;
|
|
25016
|
+
const { h: cofactor } = CURVE;
|
|
25017
|
+
_validateObject(extraOpts, {}, { uvRatio: "function" });
|
|
25018
|
+
const MASK = _2n2 << BigInt(Fn2.BYTES * 8) - _1n4;
|
|
25019
|
+
const modP = (n) => Fp2.create(n);
|
|
25020
|
+
const uvRatio2 = extraOpts.uvRatio || ((u, v) => {
|
|
25021
|
+
try {
|
|
25022
|
+
return { isValid: true, value: Fp2.sqrt(Fp2.div(u, v)) };
|
|
25023
|
+
} catch (e) {
|
|
25024
|
+
return { isValid: false, value: _0n4 };
|
|
25025
|
+
}
|
|
25026
|
+
});
|
|
25027
|
+
if (!isEdValidXY(Fp2, CURVE, CURVE.Gx, CURVE.Gy))
|
|
25028
|
+
throw new Error("bad curve params: generator point");
|
|
25029
|
+
function acoord(title, n, banZero = false) {
|
|
25030
|
+
const min = banZero ? _1n4 : _0n4;
|
|
25031
|
+
aInRange("coordinate " + title, n, min, MASK);
|
|
25032
|
+
return n;
|
|
25033
|
+
}
|
|
25034
|
+
function aextpoint(other) {
|
|
25035
|
+
if (!(other instanceof Point))
|
|
25036
|
+
throw new Error("ExtendedPoint expected");
|
|
25037
|
+
}
|
|
25038
|
+
const toAffineMemo = memoized((p, iz) => {
|
|
25039
|
+
const { X, Y, Z } = p;
|
|
25040
|
+
const is0 = p.is0();
|
|
25041
|
+
if (iz == null)
|
|
25042
|
+
iz = is0 ? _8n2 : Fp2.inv(Z);
|
|
25043
|
+
const x = modP(X * iz);
|
|
25044
|
+
const y = modP(Y * iz);
|
|
25045
|
+
const zz = Fp2.mul(Z, iz);
|
|
25046
|
+
if (is0)
|
|
25047
|
+
return { x: _0n4, y: _1n4 };
|
|
25048
|
+
if (zz !== _1n4)
|
|
25049
|
+
throw new Error("invZ was invalid");
|
|
25050
|
+
return { x, y };
|
|
25051
|
+
});
|
|
25052
|
+
const assertValidMemo = memoized((p) => {
|
|
25053
|
+
const { a, d } = CURVE;
|
|
25054
|
+
if (p.is0())
|
|
25055
|
+
throw new Error("bad point: ZERO");
|
|
25056
|
+
const { X, Y, Z, T } = p;
|
|
25057
|
+
const X2 = modP(X * X);
|
|
25058
|
+
const Y2 = modP(Y * Y);
|
|
25059
|
+
const Z2 = modP(Z * Z);
|
|
25060
|
+
const Z4 = modP(Z2 * Z2);
|
|
25061
|
+
const aX2 = modP(X2 * a);
|
|
25062
|
+
const left = modP(Z2 * modP(aX2 + Y2));
|
|
25063
|
+
const right = modP(Z4 + modP(d * modP(X2 * Y2)));
|
|
25064
|
+
if (left !== right)
|
|
25065
|
+
throw new Error("bad point: equation left != right (1)");
|
|
25066
|
+
const XY = modP(X * Y);
|
|
25067
|
+
const ZT = modP(Z * T);
|
|
25068
|
+
if (XY !== ZT)
|
|
25069
|
+
throw new Error("bad point: equation left != right (2)");
|
|
25070
|
+
return true;
|
|
25071
|
+
});
|
|
25072
|
+
class Point {
|
|
25073
|
+
constructor(X, Y, Z, T) {
|
|
25074
|
+
this.X = acoord("x", X);
|
|
25075
|
+
this.Y = acoord("y", Y);
|
|
25076
|
+
this.Z = acoord("z", Z, true);
|
|
25077
|
+
this.T = acoord("t", T);
|
|
25078
|
+
Object.freeze(this);
|
|
25079
|
+
}
|
|
25080
|
+
static CURVE() {
|
|
25081
|
+
return CURVE;
|
|
25082
|
+
}
|
|
25083
|
+
static fromAffine(p) {
|
|
25084
|
+
if (p instanceof Point)
|
|
25085
|
+
throw new Error("extended point not allowed");
|
|
25086
|
+
const { x, y } = p || {};
|
|
25087
|
+
acoord("x", x);
|
|
25088
|
+
acoord("y", y);
|
|
25089
|
+
return new Point(x, y, _1n4, modP(x * y));
|
|
25090
|
+
}
|
|
25091
|
+
// Uses algo from RFC8032 5.1.3.
|
|
25092
|
+
static fromBytes(bytes, zip215 = false) {
|
|
25093
|
+
const len = Fp2.BYTES;
|
|
25094
|
+
const { a, d } = CURVE;
|
|
25095
|
+
bytes = copyBytes(_abytes2(bytes, len, "point"));
|
|
25096
|
+
_abool2(zip215, "zip215");
|
|
25097
|
+
const normed = copyBytes(bytes);
|
|
25098
|
+
const lastByte = bytes[len - 1];
|
|
25099
|
+
normed[len - 1] = lastByte & ~128;
|
|
25100
|
+
const y = bytesToNumberLE(normed);
|
|
25101
|
+
const max = zip215 ? MASK : Fp2.ORDER;
|
|
25102
|
+
aInRange("point.y", y, _0n4, max);
|
|
25103
|
+
const y2 = modP(y * y);
|
|
25104
|
+
const u = modP(y2 - _1n4);
|
|
25105
|
+
const v = modP(d * y2 - a);
|
|
25106
|
+
let { isValid, value: x } = uvRatio2(u, v);
|
|
25107
|
+
if (!isValid)
|
|
25108
|
+
throw new Error("bad point: invalid y coordinate");
|
|
25109
|
+
const isXOdd = (x & _1n4) === _1n4;
|
|
25110
|
+
const isLastByteOdd = (lastByte & 128) !== 0;
|
|
25111
|
+
if (!zip215 && x === _0n4 && isLastByteOdd)
|
|
25112
|
+
throw new Error("bad point: x=0 and x_0=1");
|
|
25113
|
+
if (isLastByteOdd !== isXOdd)
|
|
25114
|
+
x = modP(-x);
|
|
25115
|
+
return Point.fromAffine({ x, y });
|
|
25116
|
+
}
|
|
25117
|
+
static fromHex(bytes, zip215 = false) {
|
|
25118
|
+
return Point.fromBytes(ensureBytes("point", bytes), zip215);
|
|
25119
|
+
}
|
|
25120
|
+
get x() {
|
|
25121
|
+
return this.toAffine().x;
|
|
25122
|
+
}
|
|
25123
|
+
get y() {
|
|
25124
|
+
return this.toAffine().y;
|
|
25125
|
+
}
|
|
25126
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
25127
|
+
wnaf.createCache(this, windowSize);
|
|
25128
|
+
if (!isLazy)
|
|
25129
|
+
this.multiply(_2n2);
|
|
25130
|
+
return this;
|
|
25131
|
+
}
|
|
25132
|
+
// Useful in fromAffine() - not for fromBytes(), which always created valid points.
|
|
25133
|
+
assertValidity() {
|
|
25134
|
+
assertValidMemo(this);
|
|
25135
|
+
}
|
|
25136
|
+
// Compare one point to another.
|
|
25137
|
+
equals(other) {
|
|
25138
|
+
aextpoint(other);
|
|
25139
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
25140
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
25141
|
+
const X1Z2 = modP(X1 * Z2);
|
|
25142
|
+
const X2Z1 = modP(X2 * Z1);
|
|
25143
|
+
const Y1Z2 = modP(Y1 * Z2);
|
|
25144
|
+
const Y2Z1 = modP(Y2 * Z1);
|
|
25145
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
25146
|
+
}
|
|
25147
|
+
is0() {
|
|
25148
|
+
return this.equals(Point.ZERO);
|
|
25149
|
+
}
|
|
25150
|
+
negate() {
|
|
25151
|
+
return new Point(modP(-this.X), this.Y, this.Z, modP(-this.T));
|
|
25152
|
+
}
|
|
25153
|
+
// Fast algo for doubling Extended Point.
|
|
25154
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
25155
|
+
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
|
25156
|
+
double() {
|
|
25157
|
+
const { a } = CURVE;
|
|
25158
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
25159
|
+
const A = modP(X1 * X1);
|
|
25160
|
+
const B = modP(Y1 * Y1);
|
|
25161
|
+
const C = modP(_2n2 * modP(Z1 * Z1));
|
|
25162
|
+
const D = modP(a * A);
|
|
25163
|
+
const x1y1 = X1 + Y1;
|
|
25164
|
+
const E = modP(modP(x1y1 * x1y1) - A - B);
|
|
25165
|
+
const G = D + B;
|
|
25166
|
+
const F = G - C;
|
|
25167
|
+
const H = D - B;
|
|
25168
|
+
const X3 = modP(E * F);
|
|
25169
|
+
const Y3 = modP(G * H);
|
|
25170
|
+
const T3 = modP(E * H);
|
|
25171
|
+
const Z3 = modP(F * G);
|
|
25172
|
+
return new Point(X3, Y3, Z3, T3);
|
|
25173
|
+
}
|
|
25174
|
+
// Fast algo for adding 2 Extended Points.
|
|
25175
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
|
25176
|
+
// Cost: 9M + 1*a + 1*d + 7add.
|
|
25177
|
+
add(other) {
|
|
25178
|
+
aextpoint(other);
|
|
25179
|
+
const { a, d } = CURVE;
|
|
25180
|
+
const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
|
|
25181
|
+
const { X: X2, Y: Y2, Z: Z2, T: T2 } = other;
|
|
25182
|
+
const A = modP(X1 * X2);
|
|
25183
|
+
const B = modP(Y1 * Y2);
|
|
25184
|
+
const C = modP(T1 * d * T2);
|
|
25185
|
+
const D = modP(Z1 * Z2);
|
|
25186
|
+
const E = modP((X1 + Y1) * (X2 + Y2) - A - B);
|
|
25187
|
+
const F = D - C;
|
|
25188
|
+
const G = D + C;
|
|
25189
|
+
const H = modP(B - a * A);
|
|
25190
|
+
const X3 = modP(E * F);
|
|
25191
|
+
const Y3 = modP(G * H);
|
|
25192
|
+
const T3 = modP(E * H);
|
|
25193
|
+
const Z3 = modP(F * G);
|
|
25194
|
+
return new Point(X3, Y3, Z3, T3);
|
|
25195
|
+
}
|
|
25196
|
+
subtract(other) {
|
|
25197
|
+
return this.add(other.negate());
|
|
25198
|
+
}
|
|
25199
|
+
// Constant-time multiplication.
|
|
25200
|
+
multiply(scalar) {
|
|
25201
|
+
if (!Fn2.isValidNot0(scalar))
|
|
25202
|
+
throw new Error("invalid scalar: expected 1 <= sc < curve.n");
|
|
25203
|
+
const { p, f } = wnaf.cached(this, scalar, (p2) => normalizeZ(Point, p2));
|
|
25204
|
+
return normalizeZ(Point, [p, f])[0];
|
|
25205
|
+
}
|
|
25206
|
+
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
25207
|
+
// It's faster, but should only be used when you don't care about
|
|
25208
|
+
// an exposed private key e.g. sig verification.
|
|
25209
|
+
// Does NOT allow scalars higher than CURVE.n.
|
|
25210
|
+
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
|
|
25211
|
+
multiplyUnsafe(scalar, acc = Point.ZERO) {
|
|
25212
|
+
if (!Fn2.isValid(scalar))
|
|
25213
|
+
throw new Error("invalid scalar: expected 0 <= sc < curve.n");
|
|
25214
|
+
if (scalar === _0n4)
|
|
25215
|
+
return Point.ZERO;
|
|
25216
|
+
if (this.is0() || scalar === _1n4)
|
|
25217
|
+
return this;
|
|
25218
|
+
return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p), acc);
|
|
25219
|
+
}
|
|
25220
|
+
// Checks if point is of small order.
|
|
25221
|
+
// If you add something to small order point, you will have "dirty"
|
|
25222
|
+
// point with torsion component.
|
|
25223
|
+
// Multiplies point by cofactor and checks if the result is 0.
|
|
25224
|
+
isSmallOrder() {
|
|
25225
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
25226
|
+
}
|
|
25227
|
+
// Multiplies point by curve order and checks if the result is 0.
|
|
25228
|
+
// Returns `false` is the point is dirty.
|
|
25229
|
+
isTorsionFree() {
|
|
25230
|
+
return wnaf.unsafe(this, CURVE.n).is0();
|
|
25231
|
+
}
|
|
25232
|
+
// Converts Extended point to default (x, y) coordinates.
|
|
25233
|
+
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
25234
|
+
toAffine(invertedZ) {
|
|
25235
|
+
return toAffineMemo(this, invertedZ);
|
|
25236
|
+
}
|
|
25237
|
+
clearCofactor() {
|
|
25238
|
+
if (cofactor === _1n4)
|
|
25239
|
+
return this;
|
|
25240
|
+
return this.multiplyUnsafe(cofactor);
|
|
25241
|
+
}
|
|
25242
|
+
toBytes() {
|
|
25243
|
+
const { x, y } = this.toAffine();
|
|
25244
|
+
const bytes = Fp2.toBytes(y);
|
|
25245
|
+
bytes[bytes.length - 1] |= x & _1n4 ? 128 : 0;
|
|
25246
|
+
return bytes;
|
|
25247
|
+
}
|
|
25248
|
+
toHex() {
|
|
25249
|
+
return bytesToHex(this.toBytes());
|
|
25250
|
+
}
|
|
25251
|
+
toString() {
|
|
25252
|
+
return `<Point ${this.is0() ? "ZERO" : this.toHex()}>`;
|
|
25253
|
+
}
|
|
25254
|
+
// TODO: remove
|
|
25255
|
+
get ex() {
|
|
25256
|
+
return this.X;
|
|
25257
|
+
}
|
|
25258
|
+
get ey() {
|
|
25259
|
+
return this.Y;
|
|
25260
|
+
}
|
|
25261
|
+
get ez() {
|
|
25262
|
+
return this.Z;
|
|
25263
|
+
}
|
|
25264
|
+
get et() {
|
|
25265
|
+
return this.T;
|
|
25266
|
+
}
|
|
25267
|
+
static normalizeZ(points) {
|
|
25268
|
+
return normalizeZ(Point, points);
|
|
25269
|
+
}
|
|
25270
|
+
static msm(points, scalars) {
|
|
25271
|
+
return pippenger(Point, Fn2, points, scalars);
|
|
25272
|
+
}
|
|
25273
|
+
_setWindowSize(windowSize) {
|
|
25274
|
+
this.precompute(windowSize);
|
|
25275
|
+
}
|
|
25276
|
+
toRawBytes() {
|
|
25277
|
+
return this.toBytes();
|
|
25278
|
+
}
|
|
25279
|
+
}
|
|
25280
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n4, modP(CURVE.Gx * CURVE.Gy));
|
|
25281
|
+
Point.ZERO = new Point(_0n4, _1n4, _1n4, _0n4);
|
|
25282
|
+
Point.Fp = Fp2;
|
|
25283
|
+
Point.Fn = Fn2;
|
|
25284
|
+
const wnaf = new wNAF(Point, Fn2.BITS);
|
|
25285
|
+
Point.BASE.precompute(8);
|
|
25286
|
+
return Point;
|
|
25287
|
+
}
|
|
25288
|
+
var PrimeEdwardsPoint = class {
|
|
25289
|
+
constructor(ep) {
|
|
25290
|
+
this.ep = ep;
|
|
25291
|
+
}
|
|
25292
|
+
// Static methods that must be implemented by subclasses
|
|
25293
|
+
static fromBytes(_bytes) {
|
|
25294
|
+
notImplemented();
|
|
25295
|
+
}
|
|
25296
|
+
static fromHex(_hex) {
|
|
25297
|
+
notImplemented();
|
|
25298
|
+
}
|
|
25299
|
+
get x() {
|
|
25300
|
+
return this.toAffine().x;
|
|
25301
|
+
}
|
|
25302
|
+
get y() {
|
|
25303
|
+
return this.toAffine().y;
|
|
25304
|
+
}
|
|
25305
|
+
// Common implementations
|
|
25306
|
+
clearCofactor() {
|
|
25307
|
+
return this;
|
|
25308
|
+
}
|
|
25309
|
+
assertValidity() {
|
|
25310
|
+
this.ep.assertValidity();
|
|
25311
|
+
}
|
|
25312
|
+
toAffine(invertedZ) {
|
|
25313
|
+
return this.ep.toAffine(invertedZ);
|
|
25314
|
+
}
|
|
25315
|
+
toHex() {
|
|
25316
|
+
return bytesToHex(this.toBytes());
|
|
25317
|
+
}
|
|
25318
|
+
toString() {
|
|
25319
|
+
return this.toHex();
|
|
25320
|
+
}
|
|
25321
|
+
isTorsionFree() {
|
|
25322
|
+
return true;
|
|
25323
|
+
}
|
|
25324
|
+
isSmallOrder() {
|
|
25325
|
+
return false;
|
|
25326
|
+
}
|
|
25327
|
+
add(other) {
|
|
25328
|
+
this.assertSame(other);
|
|
25329
|
+
return this.init(this.ep.add(other.ep));
|
|
25330
|
+
}
|
|
25331
|
+
subtract(other) {
|
|
25332
|
+
this.assertSame(other);
|
|
25333
|
+
return this.init(this.ep.subtract(other.ep));
|
|
25334
|
+
}
|
|
25335
|
+
multiply(scalar) {
|
|
25336
|
+
return this.init(this.ep.multiply(scalar));
|
|
25337
|
+
}
|
|
25338
|
+
multiplyUnsafe(scalar) {
|
|
25339
|
+
return this.init(this.ep.multiplyUnsafe(scalar));
|
|
25340
|
+
}
|
|
25341
|
+
double() {
|
|
25342
|
+
return this.init(this.ep.double());
|
|
25343
|
+
}
|
|
25344
|
+
negate() {
|
|
25345
|
+
return this.init(this.ep.negate());
|
|
25346
|
+
}
|
|
25347
|
+
precompute(windowSize, isLazy) {
|
|
25348
|
+
return this.init(this.ep.precompute(windowSize, isLazy));
|
|
25349
|
+
}
|
|
25350
|
+
/** @deprecated use `toBytes` */
|
|
25351
|
+
toRawBytes() {
|
|
25352
|
+
return this.toBytes();
|
|
25353
|
+
}
|
|
25354
|
+
};
|
|
25355
|
+
function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
25356
|
+
if (typeof cHash !== "function")
|
|
25357
|
+
throw new Error('"hash" function param is required');
|
|
25358
|
+
_validateObject(eddsaOpts, {}, {
|
|
25359
|
+
adjustScalarBytes: "function",
|
|
25360
|
+
randomBytes: "function",
|
|
25361
|
+
domain: "function",
|
|
25362
|
+
prehash: "function",
|
|
25363
|
+
mapToCurve: "function"
|
|
25364
|
+
});
|
|
25365
|
+
const { prehash } = eddsaOpts;
|
|
25366
|
+
const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
|
|
25367
|
+
const randomBytes2 = eddsaOpts.randomBytes || randomBytes;
|
|
25368
|
+
const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
|
25369
|
+
const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
|
|
25370
|
+
_abool2(phflag, "phflag");
|
|
25371
|
+
if (ctx.length || phflag)
|
|
25372
|
+
throw new Error("Contexts/pre-hash are not supported");
|
|
25373
|
+
return data;
|
|
25374
|
+
});
|
|
25375
|
+
function modN_LE(hash) {
|
|
25376
|
+
return Fn2.create(bytesToNumberLE(hash));
|
|
25377
|
+
}
|
|
25378
|
+
function getPrivateScalar(key) {
|
|
25379
|
+
const len = lengths.secretKey;
|
|
25380
|
+
key = ensureBytes("private key", key, len);
|
|
25381
|
+
const hashed = ensureBytes("hashed private key", cHash(key), 2 * len);
|
|
25382
|
+
const head = adjustScalarBytes2(hashed.slice(0, len));
|
|
25383
|
+
const prefix = hashed.slice(len, 2 * len);
|
|
25384
|
+
const scalar = modN_LE(head);
|
|
25385
|
+
return { head, prefix, scalar };
|
|
25386
|
+
}
|
|
25387
|
+
function getExtendedPublicKey(secretKey) {
|
|
25388
|
+
const { head, prefix, scalar } = getPrivateScalar(secretKey);
|
|
25389
|
+
const point = BASE.multiply(scalar);
|
|
25390
|
+
const pointBytes = point.toBytes();
|
|
25391
|
+
return { head, prefix, scalar, point, pointBytes };
|
|
25392
|
+
}
|
|
25393
|
+
function getPublicKey(secretKey) {
|
|
25394
|
+
return getExtendedPublicKey(secretKey).pointBytes;
|
|
25395
|
+
}
|
|
25396
|
+
function hashDomainToScalar(context = Uint8Array.of(), ...msgs) {
|
|
25397
|
+
const msg = concatBytes(...msgs);
|
|
25398
|
+
return modN_LE(cHash(domain(msg, ensureBytes("context", context), !!prehash)));
|
|
25399
|
+
}
|
|
25400
|
+
function sign(msg, secretKey, options = {}) {
|
|
25401
|
+
msg = ensureBytes("message", msg);
|
|
25402
|
+
if (prehash)
|
|
25403
|
+
msg = prehash(msg);
|
|
25404
|
+
const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
|
|
25405
|
+
const r = hashDomainToScalar(options.context, prefix, msg);
|
|
25406
|
+
const R = BASE.multiply(r).toBytes();
|
|
25407
|
+
const k = hashDomainToScalar(options.context, R, pointBytes, msg);
|
|
25408
|
+
const s = Fn2.create(r + k * scalar);
|
|
25409
|
+
if (!Fn2.isValid(s))
|
|
25410
|
+
throw new Error("sign failed: invalid s");
|
|
25411
|
+
const rs = concatBytes(R, Fn2.toBytes(s));
|
|
25412
|
+
return _abytes2(rs, lengths.signature, "result");
|
|
25413
|
+
}
|
|
25414
|
+
const verifyOpts = { zip215: true };
|
|
25415
|
+
function verify(sig, msg, publicKey, options = verifyOpts) {
|
|
25416
|
+
const { context, zip215 } = options;
|
|
25417
|
+
const len = lengths.signature;
|
|
25418
|
+
sig = ensureBytes("signature", sig, len);
|
|
25419
|
+
msg = ensureBytes("message", msg);
|
|
25420
|
+
publicKey = ensureBytes("publicKey", publicKey, lengths.publicKey);
|
|
25421
|
+
if (zip215 !== void 0)
|
|
25422
|
+
_abool2(zip215, "zip215");
|
|
25423
|
+
if (prehash)
|
|
25424
|
+
msg = prehash(msg);
|
|
25425
|
+
const mid = len / 2;
|
|
25426
|
+
const r = sig.subarray(0, mid);
|
|
25427
|
+
const s = bytesToNumberLE(sig.subarray(mid, len));
|
|
25428
|
+
let A, R, SB;
|
|
25429
|
+
try {
|
|
25430
|
+
A = Point.fromBytes(publicKey, zip215);
|
|
25431
|
+
R = Point.fromBytes(r, zip215);
|
|
25432
|
+
SB = BASE.multiplyUnsafe(s);
|
|
25433
|
+
} catch (error) {
|
|
25434
|
+
return false;
|
|
25435
|
+
}
|
|
25436
|
+
if (!zip215 && A.isSmallOrder())
|
|
25437
|
+
return false;
|
|
25438
|
+
const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
|
|
25439
|
+
const RkA = R.add(A.multiplyUnsafe(k));
|
|
25440
|
+
return RkA.subtract(SB).clearCofactor().is0();
|
|
25441
|
+
}
|
|
25442
|
+
const _size = Fp2.BYTES;
|
|
25443
|
+
const lengths = {
|
|
25444
|
+
secretKey: _size,
|
|
25445
|
+
publicKey: _size,
|
|
25446
|
+
signature: 2 * _size,
|
|
25447
|
+
seed: _size
|
|
25448
|
+
};
|
|
25449
|
+
function randomSecretKey(seed = randomBytes2(lengths.seed)) {
|
|
25450
|
+
return _abytes2(seed, lengths.seed, "seed");
|
|
25451
|
+
}
|
|
25452
|
+
function keygen(seed) {
|
|
25453
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
25454
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
25455
|
+
}
|
|
25456
|
+
function isValidSecretKey(key) {
|
|
25457
|
+
return isBytes(key) && key.length === Fn2.BYTES;
|
|
25458
|
+
}
|
|
25459
|
+
function isValidPublicKey(key, zip215) {
|
|
25460
|
+
try {
|
|
25461
|
+
return !!Point.fromBytes(key, zip215);
|
|
25462
|
+
} catch (error) {
|
|
25463
|
+
return false;
|
|
25464
|
+
}
|
|
25465
|
+
}
|
|
25466
|
+
const utils = {
|
|
25467
|
+
getExtendedPublicKey,
|
|
25468
|
+
randomSecretKey,
|
|
25469
|
+
isValidSecretKey,
|
|
25470
|
+
isValidPublicKey,
|
|
25471
|
+
/**
|
|
25472
|
+
* Converts ed public key to x public key. Uses formula:
|
|
25473
|
+
* - ed25519:
|
|
25474
|
+
* - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
|
|
25475
|
+
* - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
|
|
25476
|
+
* - ed448:
|
|
25477
|
+
* - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
|
25478
|
+
* - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
|
25479
|
+
*/
|
|
25480
|
+
toMontgomery(publicKey) {
|
|
25481
|
+
const { y } = Point.fromBytes(publicKey);
|
|
25482
|
+
const size = lengths.publicKey;
|
|
25483
|
+
const is25519 = size === 32;
|
|
25484
|
+
if (!is25519 && size !== 57)
|
|
25485
|
+
throw new Error("only defined for 25519 and 448");
|
|
25486
|
+
const u = is25519 ? Fp2.div(_1n4 + y, _1n4 - y) : Fp2.div(y - _1n4, y + _1n4);
|
|
25487
|
+
return Fp2.toBytes(u);
|
|
25488
|
+
},
|
|
25489
|
+
toMontgomerySecret(secretKey) {
|
|
25490
|
+
const size = lengths.secretKey;
|
|
25491
|
+
_abytes2(secretKey, size);
|
|
25492
|
+
const hashed = cHash(secretKey.subarray(0, size));
|
|
25493
|
+
return adjustScalarBytes2(hashed).subarray(0, size);
|
|
25494
|
+
},
|
|
25495
|
+
/** @deprecated */
|
|
25496
|
+
randomPrivateKey: randomSecretKey,
|
|
25497
|
+
/** @deprecated */
|
|
25498
|
+
precompute(windowSize = 8, point = Point.BASE) {
|
|
25499
|
+
return point.precompute(windowSize, false);
|
|
25500
|
+
}
|
|
25501
|
+
};
|
|
25502
|
+
return Object.freeze({
|
|
25503
|
+
keygen,
|
|
25504
|
+
getPublicKey,
|
|
25505
|
+
sign,
|
|
25506
|
+
verify,
|
|
25507
|
+
utils,
|
|
25508
|
+
Point,
|
|
25509
|
+
lengths
|
|
25510
|
+
});
|
|
25511
|
+
}
|
|
25512
|
+
function _eddsa_legacy_opts_to_new(c) {
|
|
25513
|
+
const CURVE = {
|
|
25514
|
+
a: c.a,
|
|
25515
|
+
d: c.d,
|
|
25516
|
+
p: c.Fp.ORDER,
|
|
25517
|
+
n: c.n,
|
|
25518
|
+
h: c.h,
|
|
25519
|
+
Gx: c.Gx,
|
|
25520
|
+
Gy: c.Gy
|
|
25521
|
+
};
|
|
25522
|
+
const Fp2 = c.Fp;
|
|
25523
|
+
const Fn2 = Field(CURVE.n, c.nBitLength, true);
|
|
25524
|
+
const curveOpts = { Fp: Fp2, Fn: Fn2, uvRatio: c.uvRatio };
|
|
25525
|
+
const eddsaOpts = {
|
|
25526
|
+
randomBytes: c.randomBytes,
|
|
25527
|
+
adjustScalarBytes: c.adjustScalarBytes,
|
|
25528
|
+
domain: c.domain,
|
|
25529
|
+
prehash: c.prehash,
|
|
25530
|
+
mapToCurve: c.mapToCurve
|
|
25531
|
+
};
|
|
25532
|
+
return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
|
|
25533
|
+
}
|
|
25534
|
+
function _eddsa_new_output_to_legacy(c, eddsa2) {
|
|
25535
|
+
const Point = eddsa2.Point;
|
|
25536
|
+
const legacy = Object.assign({}, eddsa2, {
|
|
25537
|
+
ExtendedPoint: Point,
|
|
25538
|
+
CURVE: c,
|
|
25539
|
+
nBitLength: Point.Fn.BITS,
|
|
25540
|
+
nByteLength: Point.Fn.BYTES
|
|
25541
|
+
});
|
|
25542
|
+
return legacy;
|
|
25543
|
+
}
|
|
25544
|
+
function twistedEdwards(c) {
|
|
25545
|
+
const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
|
|
25546
|
+
const Point = edwards(CURVE, curveOpts);
|
|
25547
|
+
const EDDSA = eddsa(Point, hash, eddsaOpts);
|
|
25548
|
+
return _eddsa_new_output_to_legacy(c, EDDSA);
|
|
25549
|
+
}
|
|
25550
|
+
|
|
25551
|
+
// ../mmt-manifest-verify/node_modules/@noble/curves/esm/ed25519.js
|
|
25552
|
+
var _0n5 = /* @__PURE__ */ BigInt(0);
|
|
25553
|
+
var _1n5 = BigInt(1);
|
|
25554
|
+
var _2n3 = BigInt(2);
|
|
25555
|
+
var _3n2 = BigInt(3);
|
|
25556
|
+
var _5n2 = BigInt(5);
|
|
25557
|
+
var _8n3 = BigInt(8);
|
|
25558
|
+
var ed25519_CURVE_p = BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
|
|
25559
|
+
var ed25519_CURVE = /* @__PURE__ */ (() => ({
|
|
25560
|
+
p: ed25519_CURVE_p,
|
|
25561
|
+
n: BigInt("0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"),
|
|
25562
|
+
h: _8n3,
|
|
25563
|
+
a: BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec"),
|
|
25564
|
+
d: BigInt("0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3"),
|
|
25565
|
+
Gx: BigInt("0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a"),
|
|
25566
|
+
Gy: BigInt("0x6666666666666666666666666666666666666666666666666666666666666658")
|
|
25567
|
+
}))();
|
|
25568
|
+
function ed25519_pow_2_252_3(x) {
|
|
25569
|
+
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
|
25570
|
+
const P = ed25519_CURVE_p;
|
|
25571
|
+
const x2 = x * x % P;
|
|
25572
|
+
const b2 = x2 * x % P;
|
|
25573
|
+
const b4 = pow2(b2, _2n3, P) * b2 % P;
|
|
25574
|
+
const b5 = pow2(b4, _1n5, P) * x % P;
|
|
25575
|
+
const b10 = pow2(b5, _5n2, P) * b5 % P;
|
|
25576
|
+
const b20 = pow2(b10, _10n, P) * b10 % P;
|
|
25577
|
+
const b40 = pow2(b20, _20n, P) * b20 % P;
|
|
25578
|
+
const b80 = pow2(b40, _40n, P) * b40 % P;
|
|
25579
|
+
const b160 = pow2(b80, _80n, P) * b80 % P;
|
|
25580
|
+
const b240 = pow2(b160, _80n, P) * b80 % P;
|
|
25581
|
+
const b250 = pow2(b240, _10n, P) * b10 % P;
|
|
25582
|
+
const pow_p_5_8 = pow2(b250, _2n3, P) * x % P;
|
|
25583
|
+
return { pow_p_5_8, b2 };
|
|
25584
|
+
}
|
|
25585
|
+
function adjustScalarBytes(bytes) {
|
|
25586
|
+
bytes[0] &= 248;
|
|
25587
|
+
bytes[31] &= 127;
|
|
25588
|
+
bytes[31] |= 64;
|
|
25589
|
+
return bytes;
|
|
25590
|
+
}
|
|
25591
|
+
var ED25519_SQRT_M1 = /* @__PURE__ */ BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752");
|
|
25592
|
+
function uvRatio(u, v) {
|
|
25593
|
+
const P = ed25519_CURVE_p;
|
|
25594
|
+
const v3 = mod(v * v * v, P);
|
|
25595
|
+
const v7 = mod(v3 * v3 * v, P);
|
|
25596
|
+
const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
|
|
25597
|
+
let x = mod(u * v3 * pow, P);
|
|
25598
|
+
const vx2 = mod(v * x * x, P);
|
|
25599
|
+
const root1 = x;
|
|
25600
|
+
const root2 = mod(x * ED25519_SQRT_M1, P);
|
|
25601
|
+
const useRoot1 = vx2 === u;
|
|
25602
|
+
const useRoot2 = vx2 === mod(-u, P);
|
|
25603
|
+
const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P);
|
|
25604
|
+
if (useRoot1)
|
|
25605
|
+
x = root1;
|
|
25606
|
+
if (useRoot2 || noRoot)
|
|
25607
|
+
x = root2;
|
|
25608
|
+
if (isNegativeLE(x, P))
|
|
25609
|
+
x = mod(-x, P);
|
|
25610
|
+
return { isValid: useRoot1 || useRoot2, value: x };
|
|
25611
|
+
}
|
|
25612
|
+
var Fp = /* @__PURE__ */ (() => Field(ed25519_CURVE.p, { isLE: true }))();
|
|
25613
|
+
var Fn = /* @__PURE__ */ (() => Field(ed25519_CURVE.n, { isLE: true }))();
|
|
25614
|
+
var ed25519Defaults = /* @__PURE__ */ (() => ({
|
|
25615
|
+
...ed25519_CURVE,
|
|
25616
|
+
Fp,
|
|
25617
|
+
hash: sha512,
|
|
25618
|
+
adjustScalarBytes,
|
|
25619
|
+
// dom2
|
|
25620
|
+
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
|
25621
|
+
// Constant-time, u/√v
|
|
25622
|
+
uvRatio
|
|
25623
|
+
}))();
|
|
25624
|
+
var ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
|
|
25625
|
+
var SQRT_M1 = ED25519_SQRT_M1;
|
|
25626
|
+
var SQRT_AD_MINUS_ONE = /* @__PURE__ */ BigInt("25063068953384623474111414158702152701244531502492656460079210482610430750235");
|
|
25627
|
+
var INVSQRT_A_MINUS_D = /* @__PURE__ */ BigInt("54469307008909316920995813868745141605393597292927456921205312896311721017578");
|
|
25628
|
+
var ONE_MINUS_D_SQ = /* @__PURE__ */ BigInt("1159843021668779879193775521855586647937357759715417654439879720876111806838");
|
|
25629
|
+
var D_MINUS_ONE_SQ = /* @__PURE__ */ BigInt("40440834346308536858101042469323190826248399146238708352240133220865137265952");
|
|
25630
|
+
var invertSqrt = (number) => uvRatio(_1n5, number);
|
|
25631
|
+
var MAX_255B = /* @__PURE__ */ BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
|
25632
|
+
var bytes255ToNumberLE = (bytes) => ed25519.Point.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
|
25633
|
+
function calcElligatorRistrettoMap(r0) {
|
|
25634
|
+
const { d } = ed25519_CURVE;
|
|
25635
|
+
const P = ed25519_CURVE_p;
|
|
25636
|
+
const mod2 = (n) => Fp.create(n);
|
|
25637
|
+
const r = mod2(SQRT_M1 * r0 * r0);
|
|
25638
|
+
const Ns = mod2((r + _1n5) * ONE_MINUS_D_SQ);
|
|
25639
|
+
let c = BigInt(-1);
|
|
25640
|
+
const D = mod2((c - d * r) * mod2(r + d));
|
|
25641
|
+
let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D);
|
|
25642
|
+
let s_ = mod2(s * r0);
|
|
25643
|
+
if (!isNegativeLE(s_, P))
|
|
25644
|
+
s_ = mod2(-s_);
|
|
25645
|
+
if (!Ns_D_is_sq)
|
|
25646
|
+
s = s_;
|
|
25647
|
+
if (!Ns_D_is_sq)
|
|
25648
|
+
c = r;
|
|
25649
|
+
const Nt = mod2(c * (r - _1n5) * D_MINUS_ONE_SQ - D);
|
|
25650
|
+
const s2 = s * s;
|
|
25651
|
+
const W0 = mod2((s + s) * D);
|
|
25652
|
+
const W1 = mod2(Nt * SQRT_AD_MINUS_ONE);
|
|
25653
|
+
const W2 = mod2(_1n5 - s2);
|
|
25654
|
+
const W3 = mod2(_1n5 + s2);
|
|
25655
|
+
return new ed25519.Point(mod2(W0 * W3), mod2(W2 * W1), mod2(W1 * W3), mod2(W0 * W2));
|
|
25656
|
+
}
|
|
25657
|
+
function ristretto255_map(bytes) {
|
|
25658
|
+
abytes(bytes, 64);
|
|
25659
|
+
const r1 = bytes255ToNumberLE(bytes.subarray(0, 32));
|
|
25660
|
+
const R1 = calcElligatorRistrettoMap(r1);
|
|
25661
|
+
const r2 = bytes255ToNumberLE(bytes.subarray(32, 64));
|
|
25662
|
+
const R2 = calcElligatorRistrettoMap(r2);
|
|
25663
|
+
return new _RistrettoPoint(R1.add(R2));
|
|
25664
|
+
}
|
|
25665
|
+
var _RistrettoPoint = class __RistrettoPoint extends PrimeEdwardsPoint {
|
|
25666
|
+
constructor(ep) {
|
|
25667
|
+
super(ep);
|
|
25668
|
+
}
|
|
25669
|
+
static fromAffine(ap) {
|
|
25670
|
+
return new __RistrettoPoint(ed25519.Point.fromAffine(ap));
|
|
25671
|
+
}
|
|
25672
|
+
assertSame(other) {
|
|
25673
|
+
if (!(other instanceof __RistrettoPoint))
|
|
25674
|
+
throw new Error("RistrettoPoint expected");
|
|
25675
|
+
}
|
|
25676
|
+
init(ep) {
|
|
25677
|
+
return new __RistrettoPoint(ep);
|
|
25678
|
+
}
|
|
25679
|
+
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
|
25680
|
+
static hashToCurve(hex3) {
|
|
25681
|
+
return ristretto255_map(ensureBytes("ristrettoHash", hex3, 64));
|
|
25682
|
+
}
|
|
25683
|
+
static fromBytes(bytes) {
|
|
25684
|
+
abytes(bytes, 32);
|
|
25685
|
+
const { a, d } = ed25519_CURVE;
|
|
25686
|
+
const P = ed25519_CURVE_p;
|
|
25687
|
+
const mod2 = (n) => Fp.create(n);
|
|
25688
|
+
const s = bytes255ToNumberLE(bytes);
|
|
25689
|
+
if (!equalBytes2(Fp.toBytes(s), bytes) || isNegativeLE(s, P))
|
|
25690
|
+
throw new Error("invalid ristretto255 encoding 1");
|
|
25691
|
+
const s2 = mod2(s * s);
|
|
25692
|
+
const u1 = mod2(_1n5 + a * s2);
|
|
25693
|
+
const u2 = mod2(_1n5 - a * s2);
|
|
25694
|
+
const u1_2 = mod2(u1 * u1);
|
|
25695
|
+
const u2_2 = mod2(u2 * u2);
|
|
25696
|
+
const v = mod2(a * d * u1_2 - u2_2);
|
|
25697
|
+
const { isValid, value: I } = invertSqrt(mod2(v * u2_2));
|
|
25698
|
+
const Dx = mod2(I * u2);
|
|
25699
|
+
const Dy = mod2(I * Dx * v);
|
|
25700
|
+
let x = mod2((s + s) * Dx);
|
|
25701
|
+
if (isNegativeLE(x, P))
|
|
25702
|
+
x = mod2(-x);
|
|
25703
|
+
const y = mod2(u1 * Dy);
|
|
25704
|
+
const t = mod2(x * y);
|
|
25705
|
+
if (!isValid || isNegativeLE(t, P) || y === _0n5)
|
|
25706
|
+
throw new Error("invalid ristretto255 encoding 2");
|
|
25707
|
+
return new __RistrettoPoint(new ed25519.Point(x, y, _1n5, t));
|
|
25708
|
+
}
|
|
25709
|
+
/**
|
|
25710
|
+
* Converts ristretto-encoded string to ristretto point.
|
|
25711
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
|
|
25712
|
+
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
|
25713
|
+
*/
|
|
25714
|
+
static fromHex(hex3) {
|
|
25715
|
+
return __RistrettoPoint.fromBytes(ensureBytes("ristrettoHex", hex3, 32));
|
|
25716
|
+
}
|
|
25717
|
+
static msm(points, scalars) {
|
|
25718
|
+
return pippenger(__RistrettoPoint, ed25519.Point.Fn, points, scalars);
|
|
25719
|
+
}
|
|
25720
|
+
/**
|
|
25721
|
+
* Encodes ristretto point to Uint8Array.
|
|
25722
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
|
|
25723
|
+
*/
|
|
25724
|
+
toBytes() {
|
|
25725
|
+
let { X, Y, Z, T } = this.ep;
|
|
25726
|
+
const P = ed25519_CURVE_p;
|
|
25727
|
+
const mod2 = (n) => Fp.create(n);
|
|
25728
|
+
const u1 = mod2(mod2(Z + Y) * mod2(Z - Y));
|
|
25729
|
+
const u2 = mod2(X * Y);
|
|
25730
|
+
const u2sq = mod2(u2 * u2);
|
|
25731
|
+
const { value: invsqrt } = invertSqrt(mod2(u1 * u2sq));
|
|
25732
|
+
const D1 = mod2(invsqrt * u1);
|
|
25733
|
+
const D2 = mod2(invsqrt * u2);
|
|
25734
|
+
const zInv = mod2(D1 * D2 * T);
|
|
25735
|
+
let D;
|
|
25736
|
+
if (isNegativeLE(T * zInv, P)) {
|
|
25737
|
+
let _x = mod2(Y * SQRT_M1);
|
|
25738
|
+
let _y = mod2(X * SQRT_M1);
|
|
25739
|
+
X = _x;
|
|
25740
|
+
Y = _y;
|
|
25741
|
+
D = mod2(D1 * INVSQRT_A_MINUS_D);
|
|
25742
|
+
} else {
|
|
25743
|
+
D = D2;
|
|
25744
|
+
}
|
|
25745
|
+
if (isNegativeLE(X * zInv, P))
|
|
25746
|
+
Y = mod2(-Y);
|
|
25747
|
+
let s = mod2((Z - Y) * D);
|
|
25748
|
+
if (isNegativeLE(s, P))
|
|
25749
|
+
s = mod2(-s);
|
|
25750
|
+
return Fp.toBytes(s);
|
|
25751
|
+
}
|
|
25752
|
+
/**
|
|
25753
|
+
* Compares two Ristretto points.
|
|
25754
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
|
|
25755
|
+
*/
|
|
25756
|
+
equals(other) {
|
|
25757
|
+
this.assertSame(other);
|
|
25758
|
+
const { X: X1, Y: Y1 } = this.ep;
|
|
25759
|
+
const { X: X2, Y: Y2 } = other.ep;
|
|
25760
|
+
const mod2 = (n) => Fp.create(n);
|
|
25761
|
+
const one = mod2(X1 * Y2) === mod2(Y1 * X2);
|
|
25762
|
+
const two = mod2(Y1 * Y2) === mod2(X1 * X2);
|
|
25763
|
+
return one || two;
|
|
25764
|
+
}
|
|
25765
|
+
is0() {
|
|
25766
|
+
return this.equals(__RistrettoPoint.ZERO);
|
|
25767
|
+
}
|
|
25768
|
+
};
|
|
25769
|
+
_RistrettoPoint.BASE = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.BASE))();
|
|
25770
|
+
_RistrettoPoint.ZERO = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.ZERO))();
|
|
25771
|
+
_RistrettoPoint.Fp = /* @__PURE__ */ (() => Fp)();
|
|
25772
|
+
_RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
|
|
25773
|
+
|
|
25774
|
+
// ../mmt-manifest-verify/dist/bc-provenance-v2-receiver.js
|
|
25775
|
+
var BCPV2_LEAD_BOUND_MS = 5e3;
|
|
25776
|
+
var REPLAY_WINDOW = 64n;
|
|
25777
|
+
var BUCKET_DEPTH = 64;
|
|
25778
|
+
var BUCKET_RATE_PER_MS = 1;
|
|
25779
|
+
var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
25780
|
+
function sameBytes(a, b) {
|
|
25781
|
+
if (a.byteLength !== b.byteLength)
|
|
25782
|
+
return false;
|
|
25783
|
+
let diff = 0;
|
|
25784
|
+
for (let i = 0; i < a.byteLength; i++)
|
|
25785
|
+
diff |= a[i] ^ b[i];
|
|
25786
|
+
return diff === 0;
|
|
25787
|
+
}
|
|
25788
|
+
var BcProvenanceV2Receiver = class {
|
|
25789
|
+
maxAuthDelayMs;
|
|
25790
|
+
manifestTimerMs;
|
|
25791
|
+
widenMs;
|
|
25792
|
+
/** Datagrams that left a hold queue at expiry, per path: the loss signal for failover. */
|
|
25793
|
+
expired = { multicast: 0, unicast: 0 };
|
|
25794
|
+
/**
|
|
25795
|
+
* Unicast repair (Section 7.2.7 step 2): on a receiver that can request it, how
|
|
25796
|
+
* long ago (ms) the first packet admitted into the oldest open block with an FEC
|
|
25797
|
+
* deadline was admitted; undefined with none open. A pending entry still live
|
|
25798
|
+
* then is kept instead of expiring, and is released with the block, under the
|
|
25799
|
+
* open-block bound of [MOQ-FEC] Section 9.
|
|
25800
|
+
*/
|
|
25801
|
+
keepPendingAgeMs = null;
|
|
25802
|
+
cfg;
|
|
25803
|
+
now;
|
|
25804
|
+
verifySig;
|
|
25805
|
+
accepted;
|
|
25806
|
+
queueLimit;
|
|
25807
|
+
current = null;
|
|
25808
|
+
previous = null;
|
|
25809
|
+
graceStart = {};
|
|
25810
|
+
hashAlg = null;
|
|
25811
|
+
verified = /* @__PURE__ */ new Map();
|
|
25812
|
+
highest = { multicast: /* @__PURE__ */ new Map(), mirror: /* @__PURE__ */ new Map() };
|
|
25813
|
+
closedAt = /* @__PURE__ */ new Map();
|
|
25814
|
+
lead = null;
|
|
25815
|
+
pending = /* @__PURE__ */ new Map();
|
|
25816
|
+
admittedUntil = /* @__PURE__ */ new Map();
|
|
25817
|
+
hold = { multicast: [], unicast: [] };
|
|
25818
|
+
holdBytes = { multicast: 0, unicast: 0 };
|
|
25819
|
+
order = 0;
|
|
25820
|
+
buckets;
|
|
25821
|
+
constructor(cfg) {
|
|
25822
|
+
this.maxAuthDelayMs = cfg.maxAuthDelayMs ?? 200;
|
|
25823
|
+
this.manifestTimerMs = cfg.manifestTimerMs ?? 20;
|
|
25824
|
+
if (!(this.maxAuthDelayMs >= 50 && this.maxAuthDelayMs <= 1e3) || !(this.manifestTimerMs >= 1)) {
|
|
25825
|
+
throw new Error("BCPV2 endpoint ignored: maxAuthDelayMs or manifestTimerMs out of range");
|
|
25826
|
+
}
|
|
25827
|
+
if (!(cfg.bandwidth > 0))
|
|
25828
|
+
throw new Error("BCPV2 endpoint ignored: protected endpoint without bandwidth");
|
|
25829
|
+
const kids = new Set(cfg.keys.map((k) => k.kid));
|
|
25830
|
+
const firsts = new Set(cfg.keys.map((k) => k.firstEpoch));
|
|
25831
|
+
if (cfg.keys.length < 1 || kids.size !== cfg.keys.length || firsts.size !== cfg.keys.length) {
|
|
25832
|
+
throw new Error("BCPV2 key set invalid: no keys, or a repeated kid or firstEpoch");
|
|
25833
|
+
}
|
|
25834
|
+
this.cfg = cfg;
|
|
25835
|
+
this.widenMs = Math.min(Math.max(cfg.widenMs ?? 0, 0), BCPV2_LEAD_BOUND_MS);
|
|
25836
|
+
this.now = cfg.now ?? Date.now;
|
|
25837
|
+
this.verifySig = cfg.verifySignature ?? ((pk, msg, sig) => {
|
|
25838
|
+
try {
|
|
25839
|
+
return ed25519.verify(sig, msg, pk, { zip215: false });
|
|
25840
|
+
} catch {
|
|
25841
|
+
return false;
|
|
25842
|
+
}
|
|
25843
|
+
});
|
|
25844
|
+
this.accepted = cfg.acceptedHashAlgs ?? [BCPV2_HASH_ALG_SHA256];
|
|
25845
|
+
const bandwidth = Math.min(cfg.bandwidth, 1e10);
|
|
25846
|
+
this.queueLimit = {
|
|
25847
|
+
multicast: this.maxAuthDelayMs * bandwidth / 8e3,
|
|
25848
|
+
unicast: (this.maxAuthDelayMs + this.widenMs) * bandwidth / 8e3
|
|
25849
|
+
};
|
|
25850
|
+
const t = this.now();
|
|
25851
|
+
this.buckets = {
|
|
25852
|
+
multicast: { tokens: BUCKET_DEPTH, last: t, dropped: 0 },
|
|
25853
|
+
mirror: { tokens: BUCKET_DEPTH, last: t, dropped: 0 }
|
|
25854
|
+
};
|
|
25855
|
+
}
|
|
25856
|
+
get grace() {
|
|
25857
|
+
return this.maxAuthDelayMs + 2 * this.manifestTimerMs;
|
|
25858
|
+
}
|
|
25859
|
+
/** The mirror's lead in force: 0 without a manifest track, the bound until one is measured. */
|
|
25860
|
+
get leadMs() {
|
|
25861
|
+
if (!this.cfg.applyLead)
|
|
25862
|
+
return 0;
|
|
25863
|
+
return this.lead ?? BCPV2_LEAD_BOUND_MS;
|
|
25864
|
+
}
|
|
25865
|
+
/** Step 1: a record datagram on `manifestPort`, or a record object of the manifest track. */
|
|
25866
|
+
onRecord(bytes, path) {
|
|
25867
|
+
const now = this.now();
|
|
25868
|
+
this.expire(now);
|
|
25869
|
+
const drop = (verdict) => ({ verdict, admitted: [] });
|
|
25870
|
+
const len = bytes.byteLength;
|
|
25871
|
+
if (len < BCPV2_MIN_RECORD_LENGTH || len > BCPV2_MAX_RECORD_LENGTH || (len - 132) % 32 !== 0)
|
|
25872
|
+
return drop("length");
|
|
25873
|
+
const view2 = new DataView(bytes.buffer, bytes.byteOffset, len);
|
|
25874
|
+
if (view2.getUint32(0) !== 1296583986)
|
|
25875
|
+
return drop("magic");
|
|
25876
|
+
const kid = view2.getUint32(4);
|
|
25877
|
+
const epoch = view2.getUint32(8);
|
|
25878
|
+
const mseq = view2.getBigUint64(12);
|
|
25879
|
+
const count = view2.getUint16(28);
|
|
25880
|
+
const hashAlg = bytes[30];
|
|
25881
|
+
const sent = view2.getUint32(32);
|
|
25882
|
+
const key = this.cfg.keys.find((k) => k.kid === kid);
|
|
25883
|
+
if (!key)
|
|
25884
|
+
return drop("kid");
|
|
25885
|
+
const next = Math.min(...this.cfg.keys.map((k) => k.firstEpoch).filter((f) => f > key.firstEpoch), Infinity);
|
|
25886
|
+
if (epoch < key.firstEpoch || epoch >= next)
|
|
25887
|
+
return drop("epoch");
|
|
25888
|
+
const id = `${kid}:${epoch}:${mseq}`;
|
|
25889
|
+
const seen = this.verified.get(id);
|
|
25890
|
+
if (seen) {
|
|
25891
|
+
const admitted2 = hex(sha2562(bytes)) === seen.digest ? this.arrival(seen, path, now) : [];
|
|
25892
|
+
return { verdict: "duplicate", admitted: admitted2 };
|
|
25893
|
+
}
|
|
25894
|
+
const ke = `${kid}:${epoch}`;
|
|
25895
|
+
if (this.current === null) {
|
|
25896
|
+
if (epoch < this.cfg.minEpoch || epoch < (this.cfg.pinnedEpochFloor?.get(kid) ?? 0))
|
|
25897
|
+
return drop("epoch");
|
|
25898
|
+
} else if (epoch < this.current.epoch && !this.previousAdmitted(epoch, path, now)) {
|
|
25899
|
+
return drop("epoch");
|
|
25900
|
+
}
|
|
25901
|
+
const floor = this.floor(ke, path);
|
|
25902
|
+
if (floor !== void 0 && mseq <= floor - REPLAY_WINDOW)
|
|
25903
|
+
return drop("mseq");
|
|
25904
|
+
const closed = this.closedAt.get(ke);
|
|
25905
|
+
if (closed !== void 0 && mseq > closed)
|
|
25906
|
+
return drop("closed");
|
|
25907
|
+
if (this.cfg.checkFreshness !== false) {
|
|
25908
|
+
const behind = 7e3 + this.maxAuthDelayMs + (path === "mirror" ? this.widenMs : 0);
|
|
25909
|
+
if (sent * 1e3 < now - behind || sent * 1e3 > now + 6e3)
|
|
25910
|
+
return drop("sent");
|
|
25911
|
+
}
|
|
25912
|
+
if (count !== (len - 132) / 32)
|
|
25913
|
+
return drop("count");
|
|
25914
|
+
if (!this.accepted.includes(hashAlg) || this.hashAlg !== null && hashAlg !== this.hashAlg)
|
|
25915
|
+
return drop("hash_alg");
|
|
25916
|
+
if (this.cfg.catalogCtx && !sameBytes(bytes.subarray(36, 68), this.cfg.catalogCtx))
|
|
25917
|
+
return drop("ctx");
|
|
25918
|
+
if (!this.take(path, now))
|
|
25919
|
+
return drop("budget");
|
|
25920
|
+
const record = decodeBcProvenanceV2Record(bytes);
|
|
25921
|
+
if (!this.verifySig(key.publicKey, bcProvenanceV2SignedMessage(record, this.cfg.endpoint), record.signature)) {
|
|
25922
|
+
return drop("signature");
|
|
25923
|
+
}
|
|
25924
|
+
if (this.current === null || epoch > this.current.epoch) {
|
|
25925
|
+
if (this.current !== null) {
|
|
25926
|
+
this.previous = { epoch: this.current.epoch, deadline: now + BCPV2_LEAD_BOUND_MS + this.grace };
|
|
25927
|
+
}
|
|
25928
|
+
this.current = { epoch, since: now };
|
|
25929
|
+
this.graceStart = { [path]: now };
|
|
25930
|
+
if (this.hashAlg === null) {
|
|
25931
|
+
this.hashAlg = hashAlg;
|
|
25932
|
+
for (const queue of Object.values(this.hold)) {
|
|
25933
|
+
for (const held of queue)
|
|
25934
|
+
held.digest ??= this.digest(held.datagram);
|
|
25935
|
+
}
|
|
25936
|
+
}
|
|
25937
|
+
} else if (epoch === this.current.epoch) {
|
|
25938
|
+
this.graceStart[path] ??= now;
|
|
25939
|
+
}
|
|
25940
|
+
const entry = {
|
|
25941
|
+
kid,
|
|
25942
|
+
epoch,
|
|
25943
|
+
mseq,
|
|
25944
|
+
path,
|
|
25945
|
+
arrival: now,
|
|
25946
|
+
sent,
|
|
25947
|
+
digest: hex(sha2562(bytes)),
|
|
25948
|
+
pairs: record.digests.map((d, i) => ({ digest: hex(d), seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
25949
|
+
awaitingMulticast: path === "mirror"
|
|
25950
|
+
};
|
|
25951
|
+
this.verified.set(id, entry);
|
|
25952
|
+
this.raise(ke, path, mseq);
|
|
25953
|
+
if (record.flags & 1)
|
|
25954
|
+
this.closedAt.set(ke, mseq);
|
|
25955
|
+
const widen = path === "mirror" ? this.cfg.applyLead ? this.leadMs : this.widenMs : 0;
|
|
25956
|
+
const admitted = [];
|
|
25957
|
+
for (const pair of entry.pairs)
|
|
25958
|
+
this.handlePair(pair, now, now + this.grace + widen, now, admitted);
|
|
25959
|
+
this.dropAdmittedHeld();
|
|
25960
|
+
this.prune(now);
|
|
25961
|
+
return { verdict: "verified", admitted };
|
|
25962
|
+
}
|
|
25963
|
+
/** Step 2: a data datagram on `port` (or a packet of a listed track over MoQ unicast). */
|
|
25964
|
+
onDatagram(datagram, path) {
|
|
25965
|
+
const now = this.now();
|
|
25966
|
+
this.expire(now);
|
|
25967
|
+
if (this.hashAlg !== null) {
|
|
25968
|
+
const d = this.digest(datagram);
|
|
25969
|
+
if (this.consumePending(d, now))
|
|
25970
|
+
return { verdict: "admitted", admitted: [datagram] };
|
|
25971
|
+
if (this.admittedUntil.has(d))
|
|
25972
|
+
return { verdict: "duplicate", admitted: [] };
|
|
25973
|
+
return this.enqueue({ digest: d, datagram, expiry: 0, order: 0 }, path, now);
|
|
25974
|
+
}
|
|
25975
|
+
return this.enqueue({ datagram, expiry: 0, order: 0 }, path, now);
|
|
25976
|
+
}
|
|
25977
|
+
/**
|
|
25978
|
+
* A source packet recovered by FEC decode, its base SS_ID (SS_Start + ESI)
|
|
25979
|
+
* appended: admitted only on a pending entry it consumes, never held. One no
|
|
25980
|
+
* verified record lists, or a copy of an admitted packet, is dropped.
|
|
25981
|
+
*/
|
|
25982
|
+
admitRecovered(datagram) {
|
|
25983
|
+
const now = this.now();
|
|
25984
|
+
this.expire(now);
|
|
25985
|
+
return this.hashAlg !== null && this.consumePending(this.digest(datagram), now);
|
|
25986
|
+
}
|
|
25987
|
+
/**
|
|
25988
|
+
* Discard the per-flow state on a `streamId` change or a key-set change that fails the
|
|
25989
|
+
* flow's current (kid, epoch): budgets and hold queues survive, held digests are cleared.
|
|
25990
|
+
*/
|
|
25991
|
+
resetGeneration() {
|
|
25992
|
+
this.current = null;
|
|
25993
|
+
this.previous = null;
|
|
25994
|
+
this.graceStart = {};
|
|
25995
|
+
this.hashAlg = null;
|
|
25996
|
+
this.verified.clear();
|
|
25997
|
+
this.highest = { multicast: /* @__PURE__ */ new Map(), mirror: /* @__PURE__ */ new Map() };
|
|
25998
|
+
this.closedAt.clear();
|
|
25999
|
+
this.lead = null;
|
|
26000
|
+
this.pending.clear();
|
|
26001
|
+
this.admittedUntil.clear();
|
|
26002
|
+
for (const queue of Object.values(this.hold))
|
|
26003
|
+
for (const held of queue)
|
|
26004
|
+
held.digest = void 0;
|
|
26005
|
+
}
|
|
26006
|
+
/** The receiver ceased to hold the endpoint: drop everything; no drop here is an expiry. */
|
|
26007
|
+
release() {
|
|
26008
|
+
this.resetGeneration();
|
|
26009
|
+
this.hold = { multicast: [], unicast: [] };
|
|
26010
|
+
this.holdBytes = { multicast: 0, unicast: 0 };
|
|
26011
|
+
}
|
|
26012
|
+
digest(datagram) {
|
|
26013
|
+
return hex(bcProvenanceV2DatagramDigest(this.cfg.endpoint, datagram));
|
|
26014
|
+
}
|
|
26015
|
+
/** Consume the lowest-seq pending entry for `digest`, admitting it. */
|
|
26016
|
+
consumePending(digest, now) {
|
|
26017
|
+
const list = this.pending.get(digest);
|
|
26018
|
+
if (!list?.length)
|
|
26019
|
+
return false;
|
|
26020
|
+
list.sort((a, b) => a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0);
|
|
26021
|
+
const hit = list.shift();
|
|
26022
|
+
if (list.length === 0)
|
|
26023
|
+
this.pending.delete(digest);
|
|
26024
|
+
hit.pair.pending = void 0;
|
|
26025
|
+
hit.pair.admitted = true;
|
|
26026
|
+
this.admittedUntil.set(digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
26027
|
+
return true;
|
|
26028
|
+
}
|
|
26029
|
+
enqueue(held, path, now) {
|
|
26030
|
+
if (this.holdBytes[path] + held.datagram.byteLength > this.queueLimit[path]) {
|
|
26031
|
+
return { verdict: "overflow", admitted: [] };
|
|
26032
|
+
}
|
|
26033
|
+
held.expiry = now + this.maxAuthDelayMs + (path === "unicast" ? this.widenMs : 0);
|
|
26034
|
+
held.order = this.order++;
|
|
26035
|
+
this.hold[path].push(held);
|
|
26036
|
+
this.holdBytes[path] += held.datagram.byteLength;
|
|
26037
|
+
return { verdict: "held", admitted: [] };
|
|
26038
|
+
}
|
|
26039
|
+
/** Remove and return the oldest held datagram with `digest`, across both queues. */
|
|
26040
|
+
takeHeld(digest) {
|
|
26041
|
+
let best;
|
|
26042
|
+
for (const path of ["multicast", "unicast"]) {
|
|
26043
|
+
const index = this.hold[path].findIndex((h) => h.digest === digest);
|
|
26044
|
+
if (index >= 0 && (!best || this.hold[path][index].order < best.order)) {
|
|
26045
|
+
best = { path, index, order: this.hold[path][index].order };
|
|
26046
|
+
}
|
|
26047
|
+
}
|
|
26048
|
+
if (!best)
|
|
26049
|
+
return void 0;
|
|
26050
|
+
const [held] = this.hold[best.path].splice(best.index, 1);
|
|
26051
|
+
this.holdBytes[best.path] -= held.datagram.byteLength;
|
|
26052
|
+
return held.datagram;
|
|
26053
|
+
}
|
|
26054
|
+
dropAdmittedHeld() {
|
|
26055
|
+
for (const path of ["multicast", "unicast"]) {
|
|
26056
|
+
this.hold[path] = this.hold[path].filter((h) => {
|
|
26057
|
+
const keep = h.digest === void 0 || !this.admittedUntil.has(h.digest);
|
|
26058
|
+
if (!keep)
|
|
26059
|
+
this.holdBytes[path] -= h.datagram.byteLength;
|
|
26060
|
+
return keep;
|
|
26061
|
+
});
|
|
26062
|
+
}
|
|
26063
|
+
}
|
|
26064
|
+
/** Step 1 pair handling: admit a held copy, or insert a pending entry. */
|
|
26065
|
+
handlePair(pair, now, expiry, inserted, out) {
|
|
26066
|
+
const datagram = this.takeHeld(pair.digest);
|
|
26067
|
+
if (datagram) {
|
|
26068
|
+
pair.admitted = true;
|
|
26069
|
+
this.admittedUntil.set(pair.digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
26070
|
+
out.push(datagram);
|
|
26071
|
+
return;
|
|
26072
|
+
}
|
|
26073
|
+
const entry = { pair, inserted, expiry };
|
|
26074
|
+
pair.pending = entry;
|
|
26075
|
+
const list = this.pending.get(pair.digest);
|
|
26076
|
+
if (list)
|
|
26077
|
+
list.push(entry);
|
|
26078
|
+
else
|
|
26079
|
+
this.pending.set(pair.digest, [entry]);
|
|
26080
|
+
}
|
|
26081
|
+
/** A byte-identical copy dropped by the already-verified lookup. */
|
|
26082
|
+
arrival(seen, path, now) {
|
|
26083
|
+
const ke = `${seen.kid}:${seen.epoch}`;
|
|
26084
|
+
this.raise(ke, path, seen.mseq);
|
|
26085
|
+
if (this.current && seen.epoch === this.current.epoch)
|
|
26086
|
+
this.graceStart[path] ??= now;
|
|
26087
|
+
if (path !== "multicast" || !seen.awaitingMulticast)
|
|
26088
|
+
return [];
|
|
26089
|
+
seen.awaitingMulticast = false;
|
|
26090
|
+
if (!this.cfg.applyLead)
|
|
26091
|
+
return [];
|
|
26092
|
+
const t = this.manifestTimerMs;
|
|
26093
|
+
const interval = Math.min(Math.ceil((now - seen.arrival) / t) * t, BCPV2_LEAD_BOUND_MS);
|
|
26094
|
+
if (this.lead === null) {
|
|
26095
|
+
this.lead = interval;
|
|
26096
|
+
return [];
|
|
26097
|
+
}
|
|
26098
|
+
if (interval <= this.lead)
|
|
26099
|
+
return [];
|
|
26100
|
+
this.lead = interval;
|
|
26101
|
+
const out = [];
|
|
26102
|
+
const records = [seen, ...[...this.verified.values()].filter((v) => v.awaitingMulticast && v.arrival >= now - interval)];
|
|
26103
|
+
for (const v of records) {
|
|
26104
|
+
for (const pair of v.pairs) {
|
|
26105
|
+
if (pair.admitted) {
|
|
26106
|
+
this.admittedUntil.set(pair.digest, now + this.maxAuthDelayMs + interval);
|
|
26107
|
+
} else if (pair.pending) {
|
|
26108
|
+
pair.pending.expiry = pair.pending.inserted + this.grace + interval;
|
|
26109
|
+
} else {
|
|
26110
|
+
this.handlePair(pair, now, now + this.grace + interval, now, out);
|
|
26111
|
+
}
|
|
26112
|
+
}
|
|
26113
|
+
}
|
|
26114
|
+
this.dropAdmittedHeld();
|
|
26115
|
+
return out;
|
|
26116
|
+
}
|
|
26117
|
+
previousAdmitted(epoch, path, now) {
|
|
26118
|
+
if (!this.previous || epoch !== this.previous.epoch)
|
|
26119
|
+
return false;
|
|
26120
|
+
const m = this.graceStart.multicast;
|
|
26121
|
+
if (path === "multicast")
|
|
26122
|
+
return m === void 0 || now <= m + this.grace;
|
|
26123
|
+
const starts = [m, this.graceStart.mirror].filter((s) => s !== void 0);
|
|
26124
|
+
return starts.length > 0 && now <= Math.min(...starts) + this.grace;
|
|
26125
|
+
}
|
|
26126
|
+
/** The replay floor this record meets: its path's, or the higher of both from the mirror. */
|
|
26127
|
+
floor(ke, path) {
|
|
26128
|
+
const m = this.highest.multicast.get(ke);
|
|
26129
|
+
if (path === "multicast")
|
|
26130
|
+
return m;
|
|
26131
|
+
const r = this.highest.mirror.get(ke);
|
|
26132
|
+
if (m === void 0)
|
|
26133
|
+
return r;
|
|
26134
|
+
return r === void 0 || m > r ? m : r;
|
|
26135
|
+
}
|
|
26136
|
+
raise(ke, path, mseq) {
|
|
26137
|
+
const h = this.highest[path].get(ke);
|
|
26138
|
+
if (h === void 0 || mseq > h)
|
|
26139
|
+
this.highest[path].set(ke, mseq);
|
|
26140
|
+
}
|
|
26141
|
+
take(path, now) {
|
|
26142
|
+
const b = this.buckets[path];
|
|
26143
|
+
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) * BUCKET_RATE_PER_MS);
|
|
26144
|
+
b.last = now;
|
|
26145
|
+
if (b.tokens < 1) {
|
|
26146
|
+
b.dropped++;
|
|
26147
|
+
return false;
|
|
26148
|
+
}
|
|
26149
|
+
b.tokens -= 1;
|
|
26150
|
+
return true;
|
|
26151
|
+
}
|
|
26152
|
+
expire(now) {
|
|
26153
|
+
for (const path of ["multicast", "unicast"]) {
|
|
26154
|
+
this.hold[path] = this.hold[path].filter((h) => {
|
|
26155
|
+
if (h.expiry > now)
|
|
26156
|
+
return true;
|
|
26157
|
+
this.holdBytes[path] -= h.datagram.byteLength;
|
|
26158
|
+
this.expired[path]++;
|
|
26159
|
+
return false;
|
|
26160
|
+
});
|
|
26161
|
+
}
|
|
26162
|
+
let keepFrom = null;
|
|
26163
|
+
for (const [d, list] of this.pending) {
|
|
26164
|
+
const live = list.filter((p) => {
|
|
26165
|
+
if (p.expiry > now)
|
|
26166
|
+
return true;
|
|
26167
|
+
if (keepFrom === null) {
|
|
26168
|
+
const age = this.keepPendingAgeMs?.();
|
|
26169
|
+
keepFrom = age === void 0 ? void 0 : now - age;
|
|
26170
|
+
}
|
|
26171
|
+
if (keepFrom !== void 0 && keepFrom !== null && p.expiry > keepFrom)
|
|
26172
|
+
return true;
|
|
26173
|
+
p.pair.pending = void 0;
|
|
26174
|
+
return false;
|
|
26175
|
+
});
|
|
26176
|
+
if (live.length)
|
|
26177
|
+
this.pending.set(d, live);
|
|
26178
|
+
else
|
|
26179
|
+
this.pending.delete(d);
|
|
26180
|
+
}
|
|
26181
|
+
for (const [d, until] of this.admittedUntil)
|
|
26182
|
+
if (until <= now)
|
|
26183
|
+
this.admittedUntil.delete(d);
|
|
26184
|
+
if (this.previous && now > this.previous.deadline)
|
|
26185
|
+
this.previous = null;
|
|
26186
|
+
}
|
|
26187
|
+
/**
|
|
26188
|
+
* Retire verified entries below their arrival path's replay floor, keeping a mirrored entry
|
|
26189
|
+
* while the freshness check would still pass its copy on `manifestPort`.
|
|
26190
|
+
* ponytail: linear scan per record; index by (kid, epoch) if retained sets grow past a few thousand.
|
|
26191
|
+
*/
|
|
26192
|
+
prune(now) {
|
|
26193
|
+
const retention = 7e3 + this.maxAuthDelayMs;
|
|
26194
|
+
for (const [id, v] of this.verified) {
|
|
26195
|
+
const h = this.highest[v.path].get(`${v.kid}:${v.epoch}`);
|
|
26196
|
+
const aboveFloor = h === void 0 || v.mseq > h - REPLAY_WINDOW;
|
|
26197
|
+
const fresh = this.cfg.checkFreshness === false ? v.arrival + retention >= now : v.sent * 1e3 + retention >= now;
|
|
26198
|
+
const current = this.current && (v.epoch === this.current.epoch || v.epoch === this.previous?.epoch);
|
|
26199
|
+
if (current && (aboveFloor || v.path === "mirror" && fresh))
|
|
26200
|
+
continue;
|
|
26201
|
+
this.verified.delete(id);
|
|
26202
|
+
}
|
|
26203
|
+
}
|
|
26204
|
+
};
|
|
26205
|
+
|
|
26206
|
+
// src/catalog-network-source.ts
|
|
26207
|
+
function normalizePort(value) {
|
|
26208
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
|
|
26209
|
+
return Number.isInteger(n) && n > 0 && n <= 65535 ? n : void 0;
|
|
26210
|
+
}
|
|
26211
|
+
function catalogAmtNetworkSource(networkSource) {
|
|
26212
|
+
const entries = Array.isArray(networkSource) ? networkSource : networkSource ? [networkSource] : [];
|
|
26213
|
+
for (const raw of entries) {
|
|
26214
|
+
if (!raw || typeof raw !== "object") continue;
|
|
26215
|
+
const ns = raw;
|
|
26216
|
+
if (ns.type !== "amt") continue;
|
|
26217
|
+
if (ns.discovery === "driad") continue;
|
|
26218
|
+
const relay = typeof ns.relay === "string" ? ns.relay.trim() : "";
|
|
23267
26219
|
if (!relay) continue;
|
|
23268
26220
|
return { relay, port: normalizePort(ns.port) ?? DEFAULT_AMT_RELAY_PORT };
|
|
23269
26221
|
}
|
|
@@ -23602,11 +26554,12 @@ function resolveCatalogTrackFec(catalog, tracks) {
|
|
|
23602
26554
|
}
|
|
23603
26555
|
|
|
23604
26556
|
// src/multicast-manager.ts
|
|
23605
|
-
function freezeIngress(tracks) {
|
|
26557
|
+
function freezeIngress(tracks, flow) {
|
|
23606
26558
|
return Object.freeze({
|
|
23607
26559
|
tracks: Object.freeze(
|
|
23608
26560
|
[...tracks].map(({ name, packetId }) => Object.freeze({ name, packetId }))
|
|
23609
|
-
)
|
|
26561
|
+
),
|
|
26562
|
+
...flow === void 0 ? {} : { flow }
|
|
23610
26563
|
});
|
|
23611
26564
|
}
|
|
23612
26565
|
function sameIdSet(a, b) {
|
|
@@ -23636,6 +26589,7 @@ function newBlockPolicyStats(packetId, catalogTrackName, epoch) {
|
|
|
23636
26589
|
repairsSkippedRttExceedsBudget: 0,
|
|
23637
26590
|
repairsSkippedPresentationDelayUnavailable: 0,
|
|
23638
26591
|
repairsSkippedRangeNoAnchor: 0,
|
|
26592
|
+
repairsSkippedGroupCannotComplete: 0,
|
|
23639
26593
|
lastRepairSkip: null,
|
|
23640
26594
|
keyframeRepairFetchesSent: 0,
|
|
23641
26595
|
rangeRepairFetchesSent: 0,
|
|
@@ -23720,7 +26674,9 @@ var REPAIR_PRIORITY_REASONS = /* @__PURE__ */ new Set([
|
|
|
23720
26674
|
var REPAIR_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
23721
26675
|
"rtt-unmeasured",
|
|
23722
26676
|
"rtt-exceeds-budget",
|
|
23723
|
-
"presentation-delay-unavailable"
|
|
26677
|
+
"presentation-delay-unavailable",
|
|
26678
|
+
"range-no-anchor",
|
|
26679
|
+
"group-cannot-complete"
|
|
23724
26680
|
]);
|
|
23725
26681
|
var REPAIR_DEADLINE_BOUNDS = /* @__PURE__ */ new Set(["wait", "presentation", "keyframe", "retention"]);
|
|
23726
26682
|
var GAP_REASONS = {
|
|
@@ -23938,7 +26894,7 @@ function altGroupKey(track) {
|
|
|
23938
26894
|
}
|
|
23939
26895
|
function repairTrackNamesBySource(tracks) {
|
|
23940
26896
|
const bySource = /* @__PURE__ */ new Map();
|
|
23941
|
-
const
|
|
26897
|
+
const add2 = (source, repair) => {
|
|
23942
26898
|
if (!source || !repair) return;
|
|
23943
26899
|
const names = bySource.get(source) ?? /* @__PURE__ */ new Set();
|
|
23944
26900
|
names.add(repair);
|
|
@@ -23946,9 +26902,9 @@ function repairTrackNamesBySource(tracks) {
|
|
|
23946
26902
|
};
|
|
23947
26903
|
for (const track of tracks) {
|
|
23948
26904
|
const repairTrack = typeof track.fec === "object" && track.fec !== null ? track.fec.repairTrack : void 0;
|
|
23949
|
-
if (typeof repairTrack === "string")
|
|
26905
|
+
if (typeof repairTrack === "string") add2(track.name, repairTrack);
|
|
23950
26906
|
if (track.packaging === "fec-repair" && Array.isArray(track.depends) && track.depends.length === 1) {
|
|
23951
|
-
|
|
26907
|
+
add2(track.depends[0], track.name);
|
|
23952
26908
|
}
|
|
23953
26909
|
}
|
|
23954
26910
|
return bySource;
|
|
@@ -23958,6 +26914,10 @@ var MulticastManager = class {
|
|
|
23958
26914
|
#router;
|
|
23959
26915
|
#hostMfuTimeoutMs;
|
|
23960
26916
|
#transport;
|
|
26917
|
+
/** The protected flow of the current join and its manifestPort subscription. */
|
|
26918
|
+
#provenanceJoin = null;
|
|
26919
|
+
#onFailback = null;
|
|
26920
|
+
#failbackHooked = false;
|
|
23961
26921
|
/** Live joinRepairEndpoints() releases → the repair owners they registered,
|
|
23962
26922
|
* re-applied after configureMultiTrack() clears catalog-derived owners. */
|
|
23963
26923
|
#repairReleases = /* @__PURE__ */ new Map();
|
|
@@ -24010,10 +26970,6 @@ var MulticastManager = class {
|
|
|
24010
26970
|
reassemblyPolicy: options?.reassemblyPolicy,
|
|
24011
26971
|
reassembler: options?.reassembler,
|
|
24012
26972
|
maxBufferedMfus: options?.maxBufferedMfus,
|
|
24013
|
-
manifestVerifier: options?.manifestVerifier,
|
|
24014
|
-
manifestVerifierOwnership: options?.manifestVerifierOwnership,
|
|
24015
|
-
resolveManifestIdentity: options?.resolveManifestIdentity,
|
|
24016
|
-
resolveManifestIdentityForRoute: options?.resolveManifestIdentityForRoute,
|
|
24017
26973
|
// The router has reset its own state for the stream; reset the rest.
|
|
24018
26974
|
onPublisherRestart: (packetId, catalogTrackName) => {
|
|
24019
26975
|
this.#resetStreamOutsideRouter(packetId, catalogTrackName);
|
|
@@ -24060,7 +27016,7 @@ var MulticastManager = class {
|
|
|
24060
27016
|
});
|
|
24061
27017
|
this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
|
|
24062
27018
|
this.#transport = new MulticastTransport((packet, ingress) => {
|
|
24063
|
-
this.#router.feedPacket(packet, "multicast",
|
|
27019
|
+
this.#router.feedPacket(packet, "multicast", ingress);
|
|
24064
27020
|
});
|
|
24065
27021
|
this.#transport.onDelivery((delivery) => {
|
|
24066
27022
|
this.#fec.setMulticastSymbolDelivery(delivery);
|
|
@@ -24068,17 +27024,7 @@ var MulticastManager = class {
|
|
|
24068
27024
|
this.#fec.onBlockRecovered = (sbn, packetId, data, meta, recoveredTrackName) => {
|
|
24069
27025
|
const recovered = new Uint8Array(data);
|
|
24070
27026
|
const catalogTrackName = recoveredTrackName ?? this.#fec.catalogTrackNameForBlock(sbn, packetId);
|
|
24071
|
-
|
|
24072
|
-
this.#router.routeRecoveredSymbol(sbn, recovered, packetId, meta.esi);
|
|
24073
|
-
} else {
|
|
24074
|
-
this.#router.routeRecoveredSymbol(
|
|
24075
|
-
sbn,
|
|
24076
|
-
recovered,
|
|
24077
|
-
packetId,
|
|
24078
|
-
meta.esi,
|
|
24079
|
-
catalogTrackName
|
|
24080
|
-
);
|
|
24081
|
-
}
|
|
27027
|
+
return catalogTrackName === void 0 ? this.#router.routeRecoveredSymbol(sbn, recovered, packetId, meta.esi) : this.#router.routeRecoveredSymbol(sbn, recovered, packetId, meta.esi, catalogTrackName);
|
|
24082
27028
|
};
|
|
24083
27029
|
this.#fec.onRepairResolved = (sbn, packetId, _outcome, catalogTrackName) => {
|
|
24084
27030
|
this.clearRepairForBlock(sbn, packetId, catalogTrackName);
|
|
@@ -24153,6 +27099,10 @@ var MulticastManager = class {
|
|
|
24153
27099
|
get router() {
|
|
24154
27100
|
return this.#router;
|
|
24155
27101
|
}
|
|
27102
|
+
/** Repair FETCH wiring (wireRepairFetch): this manager can request unicast repair. */
|
|
27103
|
+
setUnicastRepair(enabled) {
|
|
27104
|
+
this.#router.setUnicastRepair(enabled);
|
|
27105
|
+
}
|
|
24156
27106
|
get transport() {
|
|
24157
27107
|
return this.#transport;
|
|
24158
27108
|
}
|
|
@@ -24563,8 +27513,35 @@ var MulticastManager = class {
|
|
|
24563
27513
|
onTakeover(cb) {
|
|
24564
27514
|
this.#transport.onTakeover(cb);
|
|
24565
27515
|
}
|
|
27516
|
+
/** Failback un-protects the join's flow (7.2.7 failover), so the fallback's MoQ packets are read. */
|
|
24566
27517
|
onFailback(cb) {
|
|
24567
|
-
this.#
|
|
27518
|
+
this.#onFailback = cb;
|
|
27519
|
+
this.#hookFailback();
|
|
27520
|
+
}
|
|
27521
|
+
/**
|
|
27522
|
+
* Install the manager's own failback listener on the media transport, which
|
|
27523
|
+
* arms its silence watchdog: a protected join un-protects on failover even
|
|
27524
|
+
* when no player listens, else its tracks' MoQ copies stay dropped.
|
|
27525
|
+
*/
|
|
27526
|
+
#hookFailback() {
|
|
27527
|
+
if (this.#failbackHooked) return;
|
|
27528
|
+
this.#failbackHooked = true;
|
|
27529
|
+
this.#transport.onFailback((reason) => {
|
|
27530
|
+
const joined = this.#provenanceJoin;
|
|
27531
|
+
if (joined !== null) this.#router.unprotectProvenanceFlow(joined.flow);
|
|
27532
|
+
this.#onFailback?.(reason);
|
|
27533
|
+
});
|
|
27534
|
+
}
|
|
27535
|
+
/**
|
|
27536
|
+
* A protected join's handoff (draft -01 7.2.7), before `onTakeover`: its
|
|
27537
|
+
* first admitted datagram. MoQ copies of the flow's tracks the router still
|
|
27538
|
+
* held unadmitted are discarded, and later ones are dropped. Fires again
|
|
27539
|
+
* after a failback once multicast resumes.
|
|
27540
|
+
*/
|
|
27541
|
+
onProtected(cb) {
|
|
27542
|
+
this.#router.onProvenanceFlowProtected(cb && ((flow) => {
|
|
27543
|
+
if (this.#provenanceJoin?.flow === flow) cb();
|
|
27544
|
+
}));
|
|
24568
27545
|
}
|
|
24569
27546
|
/** See MulticastTransport.onDataPlaneLive: the join's first accepted packet. */
|
|
24570
27547
|
onDataPlaneLive(cb) {
|
|
@@ -24580,6 +27557,12 @@ var MulticastManager = class {
|
|
|
24580
27557
|
return MulticastTransport.isAvailable();
|
|
24581
27558
|
}
|
|
24582
27559
|
async join(config) {
|
|
27560
|
+
const provenanceFlow = config.provenance?.flow;
|
|
27561
|
+
if (provenanceFlow !== void 0 && ((config.sourceAddress ?? config.senderAddress) !== provenanceFlow.source || config.groupAddress !== provenanceFlow.groupAddress || config.port !== provenanceFlow.port)) {
|
|
27562
|
+
throw new RangeError(
|
|
27563
|
+
"[MulticastManager] join: provenance flow (S, G, port) must match sourceAddress ?? senderAddress, groupAddress and port"
|
|
27564
|
+
);
|
|
27565
|
+
}
|
|
24583
27566
|
const staged = this.#renditionSwitch;
|
|
24584
27567
|
if (staged !== null) this.#endRenditionSwitch(staged, { reason: "superseded" }, false);
|
|
24585
27568
|
this.configureFec(config);
|
|
@@ -24587,16 +27570,21 @@ var MulticastManager = class {
|
|
|
24587
27570
|
const selectedPacketIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
24588
27571
|
const catalogTracks = config.tracks?.filter(({ packetId }) => selectedPacketIds === null || selectedPacketIds.has(packetId));
|
|
24589
27572
|
const ingressTracks = catalogTracks ? catalogTracks : selectedPacketIds === null ? void 0 : [...selectedPacketIds].map((packetId) => ({ name: "", packetId }));
|
|
24590
|
-
|
|
27573
|
+
this.#releaseProvenanceJoin();
|
|
27574
|
+
const provenance = config.provenance;
|
|
27575
|
+
const protectedTracks = ingressTracks ?? provenance?.flow.tracks;
|
|
27576
|
+
const ingress = protectedTracks === void 0 ? void 0 : {
|
|
24591
27577
|
tracks: Object.freeze(
|
|
24592
|
-
|
|
27578
|
+
protectedTracks.map(
|
|
24593
27579
|
({ name, packetId }) => Object.freeze({ name, packetId })
|
|
24594
27580
|
)
|
|
24595
|
-
)
|
|
27581
|
+
),
|
|
27582
|
+
...provenance === void 0 ? {} : { flow: multicastProvenanceFlowKey(provenance.flow) }
|
|
24596
27583
|
};
|
|
24597
27584
|
const amtRelay = config.amtRelay;
|
|
24598
27585
|
const transportConfig = {
|
|
24599
27586
|
sourceAddress: config.sourceAddress,
|
|
27587
|
+
senderAddress: config.senderAddress,
|
|
24600
27588
|
groupAddress: config.groupAddress,
|
|
24601
27589
|
port: config.port,
|
|
24602
27590
|
mode: config.mode,
|
|
@@ -24623,7 +27611,61 @@ var MulticastManager = class {
|
|
|
24623
27611
|
packetIds: config.packetIds,
|
|
24624
27612
|
tracks: catalogTracks?.map(({ name, packetId }) => ({ name, packetId }))
|
|
24625
27613
|
};
|
|
24626
|
-
|
|
27614
|
+
if (provenance !== void 0) this.#joinProvenance(provenance, ingress, transportConfig);
|
|
27615
|
+
const protectedJoin = this.#provenanceJoin;
|
|
27616
|
+
const joined = await (ingress === void 0 ? this.#transport.join(transportConfig) : this.#transport.join(transportConfig, ingress));
|
|
27617
|
+
if (!joined && protectedJoin !== null && this.#provenanceJoin === protectedJoin) this.#releaseProvenanceJoin();
|
|
27618
|
+
return joined;
|
|
27619
|
+
}
|
|
27620
|
+
/**
|
|
27621
|
+
* Register the protected flow and subscribe its `manifestPort` on the same
|
|
27622
|
+
* `(S, G)` with its own listener (draft -01 7.2.7), so records reach the
|
|
27623
|
+
* flow's receiver and media datagrams are held until one admits them. A
|
|
27624
|
+
* manifest subscription that fails to join admits nothing: the media is
|
|
27625
|
+
* held and expires, never read unauthenticated.
|
|
27626
|
+
*/
|
|
27627
|
+
#joinProvenance(provenance, ingress, media) {
|
|
27628
|
+
const { flow, auth } = provenance;
|
|
27629
|
+
const receiver = new BcProvenanceV2Receiver({
|
|
27630
|
+
...provenance.receiver,
|
|
27631
|
+
// The caller's ctx when it passes one, else the catalog's: an explicit
|
|
27632
|
+
// `catalogCtx: undefined` must not switch off the 7.2.4 ctx check.
|
|
27633
|
+
catalogCtx: provenance.receiver?.catalogCtx ?? flow.ctx,
|
|
27634
|
+
endpoint: {
|
|
27635
|
+
sourceAddress: flow.source,
|
|
27636
|
+
groupAddress: flow.groupAddress,
|
|
27637
|
+
port: flow.port,
|
|
27638
|
+
manifestPort: flow.manifestPort,
|
|
27639
|
+
streamId: flow.streamId
|
|
27640
|
+
},
|
|
27641
|
+
keys: auth.keys,
|
|
27642
|
+
minEpoch: auth.minEpoch,
|
|
27643
|
+
bandwidth: flow.bandwidth,
|
|
27644
|
+
maxAuthDelayMs: flow.maxAuthDelayMs,
|
|
27645
|
+
manifestTimerMs: flow.manifestTimerMs
|
|
27646
|
+
});
|
|
27647
|
+
this.#router.registerProvenanceFlow(ingress, receiver);
|
|
27648
|
+
this.#hookFailback();
|
|
27649
|
+
const manifest = new MulticastTransport((record) => {
|
|
27650
|
+
if (this.#provenanceJoin?.manifest === manifest) {
|
|
27651
|
+
this.#router.feedProvenanceRecord(ingress.flow, record, "multicast");
|
|
27652
|
+
}
|
|
27653
|
+
});
|
|
27654
|
+
this.#provenanceJoin = { flow: ingress.flow, manifest };
|
|
27655
|
+
const { packetIds: _packetIds, tracks: _tracks, ...path } = media;
|
|
27656
|
+
void manifest.join({ ...path, port: flow.manifestPort }).then((joined) => {
|
|
27657
|
+
if (!joined && this.#provenanceJoin?.manifest === manifest) {
|
|
27658
|
+
console.warn(`[MulticastManager] manifestPort ${flow.groupAddress}:${flow.manifestPort} not joined`, manifest.lastJoinOutcome);
|
|
27659
|
+
}
|
|
27660
|
+
});
|
|
27661
|
+
}
|
|
27662
|
+
/** Un-protect: drop the flow's hold queues (no expiry counted) and leave its manifestPort. */
|
|
27663
|
+
#releaseProvenanceJoin() {
|
|
27664
|
+
const joined = this.#provenanceJoin;
|
|
27665
|
+
if (joined === null) return;
|
|
27666
|
+
this.#provenanceJoin = null;
|
|
27667
|
+
joined.manifest.dispose();
|
|
27668
|
+
this.#router.releaseProvenanceFlow(joined.flow);
|
|
24627
27669
|
}
|
|
24628
27670
|
/**
|
|
24629
27671
|
* Join the selected rung's dedicated repair groups beside the media join
|
|
@@ -24653,10 +27695,11 @@ var MulticastManager = class {
|
|
|
24653
27695
|
this.#applyRepairOwners(repairOwners);
|
|
24654
27696
|
for (const endpoint of endpoints) {
|
|
24655
27697
|
const ingress = {
|
|
24656
|
-
tracks: Object.freeze(endpoint.tracks.map(({ name, packetId }) => Object.freeze({ name, packetId })))
|
|
27698
|
+
tracks: Object.freeze(endpoint.tracks.map(({ name, packetId }) => Object.freeze({ name, packetId }))),
|
|
27699
|
+
...endpoint.flow === void 0 ? {} : { flow: endpoint.flow }
|
|
24657
27700
|
};
|
|
24658
27701
|
const onPacket = (packet) => {
|
|
24659
|
-
if (!released) this.#router.feedPacket(packet, "multicast",
|
|
27702
|
+
if (!released) this.#router.feedPacket(packet, "multicast", ingress);
|
|
24660
27703
|
};
|
|
24661
27704
|
const fail = (error) => console.warn(`[MulticastManager] repair group ${endpoint.group}:${endpoint.port} not joined`, error);
|
|
24662
27705
|
if (typeof provider === "function") {
|
|
@@ -24794,7 +27837,7 @@ var MulticastManager = class {
|
|
|
24794
27837
|
for (const track of [...live.tracks, ...request.tracks]) {
|
|
24795
27838
|
stagedTracks.set(`${track.packetId}\0${track.name}`, track);
|
|
24796
27839
|
}
|
|
24797
|
-
const stagedIngress = freezeIngress(stagedTracks.values());
|
|
27840
|
+
const stagedIngress = freezeIngress(stagedTracks.values(), live.flow);
|
|
24798
27841
|
const stagedPacketIds = committedPacketIds === void 0 || livePacketIds === null ? null : /* @__PURE__ */ new Set([...livePacketIds, ...committedPacketIds]);
|
|
24799
27842
|
const widened = stagedPacketIds !== null && !sameIdSet(stagedPacketIds, livePacketIds);
|
|
24800
27843
|
const active = {
|
|
@@ -24805,7 +27848,7 @@ var MulticastManager = class {
|
|
|
24805
27848
|
liveIngress: live,
|
|
24806
27849
|
livePacketIds,
|
|
24807
27850
|
widened,
|
|
24808
|
-
committedIngress: freezeIngress(request.tracks),
|
|
27851
|
+
committedIngress: freezeIngress(request.tracks, live.flow),
|
|
24809
27852
|
committedPacketIds,
|
|
24810
27853
|
router: null,
|
|
24811
27854
|
state: "staged"
|
|
@@ -24818,7 +27861,7 @@ var MulticastManager = class {
|
|
|
24818
27861
|
let widening;
|
|
24819
27862
|
try {
|
|
24820
27863
|
if (widened) widening = this.#transport.setPacketIds([...stagedPacketIds]);
|
|
24821
|
-
this.#
|
|
27864
|
+
this.#setSwitchIngress(stagedIngress);
|
|
24822
27865
|
} catch (error) {
|
|
24823
27866
|
active.router.cancel();
|
|
24824
27867
|
throw error;
|
|
@@ -24839,6 +27882,11 @@ var MulticastManager = class {
|
|
|
24839
27882
|
}
|
|
24840
27883
|
};
|
|
24841
27884
|
}
|
|
27885
|
+
/** Route a switch's ingress and keep a protected flow's registration in step (7.2.7). */
|
|
27886
|
+
#setSwitchIngress(ingress) {
|
|
27887
|
+
this.#transport.setContext(ingress);
|
|
27888
|
+
if (ingress.flow !== void 0) this.#router.updateProvenanceFlowIngress({ ...ingress, flow: ingress.flow });
|
|
27889
|
+
}
|
|
24842
27890
|
/** The router committed the staged switch (it already retired `from`). */
|
|
24843
27891
|
#commitRenditionSwitch(active, commit) {
|
|
24844
27892
|
if (this.#renditionSwitch !== active) return;
|
|
@@ -24846,7 +27894,7 @@ var MulticastManager = class {
|
|
|
24846
27894
|
active.state = "committed";
|
|
24847
27895
|
active.router = null;
|
|
24848
27896
|
if (this.#transport.joined) {
|
|
24849
|
-
this.#
|
|
27897
|
+
this.#setSwitchIngress(active.committedIngress);
|
|
24850
27898
|
if (active.committedPacketIds !== void 0) {
|
|
24851
27899
|
this.#transport.setPacketIds(active.committedPacketIds).catch((error) => {
|
|
24852
27900
|
console.warn(
|
|
@@ -24882,7 +27930,7 @@ var MulticastManager = class {
|
|
|
24882
27930
|
active.router = null;
|
|
24883
27931
|
if (restore) {
|
|
24884
27932
|
if (this.#transport.joined) {
|
|
24885
|
-
this.#
|
|
27933
|
+
this.#setSwitchIngress(active.liveIngress);
|
|
24886
27934
|
if (active.widened && active.livePacketIds !== null) {
|
|
24887
27935
|
this.#transport.setPacketIds([...active.livePacketIds]).catch((error) => {
|
|
24888
27936
|
console.warn(
|
|
@@ -24954,11 +28002,11 @@ var MulticastManager = class {
|
|
|
24954
28002
|
}
|
|
24955
28003
|
/** Route a Worker-recovered FEC block through the MMTP parser + dedup pipeline. */
|
|
24956
28004
|
routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
|
|
24957
|
-
this.#router.routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
|
|
28005
|
+
return this.#router.routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
|
|
24958
28006
|
}
|
|
24959
28007
|
/** Route one Worker-recovered source symbol through the MMTP parser + dedup pipeline. */
|
|
24960
28008
|
routeRecoveredSymbol(blockId, data, packetId, esi = 0, catalogTrackName) {
|
|
24961
|
-
this.#router.routeRecoveredSymbol(blockId, data, packetId, esi, catalogTrackName);
|
|
28009
|
+
return this.#router.routeRecoveredSymbol(blockId, data, packetId, esi, catalogTrackName);
|
|
24962
28010
|
}
|
|
24963
28011
|
// ---------------------------------------------------------------------------
|
|
24964
28012
|
// Phase 14: multi-track catalog ingest (D-04, D-06, D-07, D-17, D-26, D-28)
|
|
@@ -25268,6 +28316,7 @@ var MulticastManager = class {
|
|
|
25268
28316
|
if (reason === "rtt-unmeasured") stats.repairsSkippedRttUnmeasured++;
|
|
25269
28317
|
else if (reason === "rtt-exceeds-budget") stats.repairsSkippedRttExceedsBudget++;
|
|
25270
28318
|
else if (reason === "range-no-anchor") stats.repairsSkippedRangeNoAnchor++;
|
|
28319
|
+
else if (reason === "group-cannot-complete") stats.repairsSkippedGroupCannotComplete++;
|
|
25271
28320
|
else stats.repairsSkippedPresentationDelayUnavailable++;
|
|
25272
28321
|
stats.lastRepairSkip = {
|
|
25273
28322
|
reason,
|
|
@@ -25463,11 +28512,15 @@ var MulticastManager = class {
|
|
|
25463
28512
|
this.#trackResetObservers.clear();
|
|
25464
28513
|
this.#repairLifecycleObservers.clear();
|
|
25465
28514
|
this.#transport.dispose();
|
|
28515
|
+
this.#releaseProvenanceJoin();
|
|
25466
28516
|
for (const release of [...this.#repairReleases.keys()]) release();
|
|
25467
28517
|
this.#router.dispose();
|
|
25468
28518
|
this.#fec.dispose();
|
|
25469
28519
|
}
|
|
25470
28520
|
};
|
|
28521
|
+
function multicastProvenanceFlowKey(flow) {
|
|
28522
|
+
return JSON.stringify([flow.source, flow.groupAddress, flow.port]);
|
|
28523
|
+
}
|
|
25471
28524
|
|
|
25472
28525
|
// src/fec-worker-setup.ts
|
|
25473
28526
|
async function setupFecWorkerForTrack(opts) {
|
|
@@ -27279,6 +30332,9 @@ var MmtpFecSession = class {
|
|
|
27279
30332
|
/** The newest SS_ID when the remembered SS_IDs were last pruned. */
|
|
27280
30333
|
#prunedAt;
|
|
27281
30334
|
#repairPacketId;
|
|
30335
|
+
#newestSource;
|
|
30336
|
+
#heldLayers;
|
|
30337
|
+
#anchorHorizon;
|
|
27282
30338
|
#stats = {
|
|
27283
30339
|
sourceSymbols: 0,
|
|
27284
30340
|
repairSymbols: 0,
|
|
@@ -27291,11 +30347,12 @@ var MmtpFecSession = class {
|
|
|
27291
30347
|
dropped: {},
|
|
27292
30348
|
sourceSymbolsByPath: { moq: 0, multicast: 0 },
|
|
27293
30349
|
repairSymbolsByPath: { moq: 0, multicast: 0 },
|
|
27294
|
-
mixedPath: { clean: 0, recovered: 0, failed: 0, released: 0 }
|
|
30350
|
+
mixedPath: { clean: 0, recovered: 0, failed: 0, released: 0 },
|
|
30351
|
+
failClosed: null
|
|
27295
30352
|
};
|
|
27296
30353
|
/**
|
|
27297
30354
|
* @throws when the catalog block lacks or misstates a field the session
|
|
27298
|
-
* needs: the block profile, K, T, `interleaveDepthMs` or
|
|
30355
|
+
* needs: the block profile, K, P_0 (`repairSymbols`), T, `interleaveDepthMs` or
|
|
27299
30356
|
* `reorderToleranceMs`. None is defaulted.
|
|
27300
30357
|
*/
|
|
27301
30358
|
constructor(config) {
|
|
@@ -27309,6 +30366,7 @@ var MmtpFecSession = class {
|
|
|
27309
30366
|
}
|
|
27310
30367
|
this.algorithm = fec.algorithm === "reed-solomon" ? "reed-solomon" : "raptorq";
|
|
27311
30368
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
|
|
30369
|
+
this.repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
|
|
27312
30370
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
|
|
27313
30371
|
const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, "fec.interleaveDepthMs");
|
|
27314
30372
|
const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, "fec.reorderToleranceMs");
|
|
@@ -27316,6 +30374,18 @@ var MmtpFecSession = class {
|
|
|
27316
30374
|
fecBlockBudgetMs(interleaveDepthMs, reorderToleranceMs),
|
|
27317
30375
|
interleaveDepthMs
|
|
27318
30376
|
);
|
|
30377
|
+
this.#heldLayers = new HeldLayerAdmission(
|
|
30378
|
+
config.repairLayers ?? [
|
|
30379
|
+
{
|
|
30380
|
+
layer: 0,
|
|
30381
|
+
repairTrackName: typeof fec.repairTrack === "string" ? fec.repairTrack : "",
|
|
30382
|
+
rsIdOffset: 0,
|
|
30383
|
+
repairSymbols: this.repairSymbols
|
|
30384
|
+
}
|
|
30385
|
+
],
|
|
30386
|
+
config.heldLayers
|
|
30387
|
+
);
|
|
30388
|
+
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols);
|
|
27319
30389
|
if (typeof config.Decoder !== "function") {
|
|
27320
30390
|
throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
|
|
27321
30391
|
}
|
|
@@ -27338,7 +30408,8 @@ var MmtpFecSession = class {
|
|
|
27338
30408
|
dropped: { ...this.#stats.dropped },
|
|
27339
30409
|
sourceSymbolsByPath: { ...this.#stats.sourceSymbolsByPath },
|
|
27340
30410
|
repairSymbolsByPath: { ...this.#stats.repairSymbolsByPath },
|
|
27341
|
-
mixedPath: { ...this.#stats.mixedPath }
|
|
30411
|
+
mixedPath: { ...this.#stats.mixedPath },
|
|
30412
|
+
failClosed: this.#stats.failClosed && { ...this.#stats.failClosed }
|
|
27342
30413
|
};
|
|
27343
30414
|
}
|
|
27344
30415
|
/**
|
|
@@ -27363,6 +30434,7 @@ var MmtpFecSession = class {
|
|
|
27363
30434
|
this.#stats.sourceSymbols++;
|
|
27364
30435
|
this.#stats.sourceSymbolsByPath[path]++;
|
|
27365
30436
|
this.#noteNewest(ssId);
|
|
30437
|
+
if (this.#newestSource === void 0 || serial32Precedes(this.#newestSource, ssId)) this.#newestSource = ssId;
|
|
27366
30438
|
const start = this.#blockStartOf(ssId);
|
|
27367
30439
|
const block = start === void 0 ? void 0 : this.#blocks.get(start);
|
|
27368
30440
|
if (block) {
|
|
@@ -27391,14 +30463,17 @@ var MmtpFecSession = class {
|
|
|
27391
30463
|
* Feeds one object of the repair track: a whole MMTP repair packet (ISO/IEC
|
|
27392
30464
|
* 23008-1 C.5.3). A repair track carries one repair flow, so the first
|
|
27393
30465
|
* repair packet fixes its packet ID and a packet of any other is refused.
|
|
27394
|
-
* `path` is where it was received, as for addSource().
|
|
30466
|
+
* `path` is where it was received, as for addSource(). `repairTrackName`
|
|
30467
|
+
* is the repair track it was received on, for the 6.3.9 own-track rule; a
|
|
30468
|
+
* track the layers do not list is refused as `repair:unlisted-track`.
|
|
27395
30469
|
*
|
|
27396
30470
|
* @returns the protected packets this repair symbol recovered, as for
|
|
27397
30471
|
* addSource().
|
|
27398
30472
|
*/
|
|
27399
|
-
addRepair(packet, path = "moq") {
|
|
30473
|
+
addRepair(packet, path = "moq", repairTrackName) {
|
|
27400
30474
|
const decoder = this.#decoder;
|
|
27401
30475
|
if (!decoder) return this.#drop("repair:released");
|
|
30476
|
+
if (this.#stats.failClosed) return this.#drop("repair:fail-closed");
|
|
27402
30477
|
const pathBit = PATH_BITS[path];
|
|
27403
30478
|
if (!pathBit) return this.#drop("repair:unknown-path");
|
|
27404
30479
|
let header;
|
|
@@ -27419,6 +30494,18 @@ var MmtpFecSession = class {
|
|
|
27419
30494
|
if (this.algorithm === "reed-solomon" && frame.ssbLength + frame.rsbLength > 255) {
|
|
27420
30495
|
return this.#drop("repair:rs-block-length");
|
|
27421
30496
|
}
|
|
30497
|
+
const verdict = this.#heldLayers.admit(
|
|
30498
|
+
repairTrackName,
|
|
30499
|
+
frame.ssStart,
|
|
30500
|
+
frame.rsbLength,
|
|
30501
|
+
this.#anchorHorizon,
|
|
30502
|
+
this.#newestSource
|
|
30503
|
+
);
|
|
30504
|
+
if (verdict === "discard") return this.#drop("repair:unlisted-track");
|
|
30505
|
+
if (verdict !== void 0) {
|
|
30506
|
+
this.#stats.failClosed = verdict;
|
|
30507
|
+
return this.#drop("repair:fail-closed");
|
|
30508
|
+
}
|
|
27422
30509
|
const start = frame.ssStart;
|
|
27423
30510
|
let block = this.#blocks.get(start);
|
|
27424
30511
|
if (block && block.ssbLength !== frame.ssbLength) return this.#drop("repair:conflicting-ssb-length");
|
|
@@ -27638,6 +30725,7 @@ function wireRepairFetch(mc, opts) {
|
|
|
27638
30725
|
if (opts.rangeTransport !== void 0 && (typeof opts.repairEpochReference !== "function" || !mc.onUndelimitedLoss || !mc.closeIsoHoles)) {
|
|
27639
30726
|
throw new Error("[RepairFetch] rangeTransport needs repairEpochReference and a host that reports undelimited losses");
|
|
27640
30727
|
}
|
|
30728
|
+
mc.setUnicastRepair?.(true);
|
|
27641
30729
|
const entries = /* @__PURE__ */ new Map();
|
|
27642
30730
|
const warnedNoTarget = /* @__PURE__ */ new Set();
|
|
27643
30731
|
let disposed = false;
|
|
@@ -27689,12 +30777,13 @@ function wireRepairFetch(mc, opts) {
|
|
|
27689
30777
|
const admit = (entry, now) => {
|
|
27690
30778
|
const budgetMs = entry.deadlineAt - now;
|
|
27691
30779
|
const rttMs = readRtt();
|
|
27692
|
-
|
|
30780
|
+
const reason = !entry.range && !groupCanComplete(entry.sbn, entry.packetId, entry.catalogTrackName) ? "group-cannot-complete" : rttMs === null ? "rtt-unmeasured" : rttMs < budgetMs ? null : "rtt-exceeds-budget";
|
|
30781
|
+
if (reason === null) return rttMs;
|
|
27693
30782
|
retire(entry);
|
|
27694
30783
|
releaseHold(entry);
|
|
27695
30784
|
clearEntry(entry);
|
|
27696
30785
|
mc.noteRepairSkipped(
|
|
27697
|
-
|
|
30786
|
+
reason,
|
|
27698
30787
|
{ rttMs, budgetMs, priority: entry.priority },
|
|
27699
30788
|
entry.sbn,
|
|
27700
30789
|
entry.packetId,
|
|
@@ -28123,6 +31212,7 @@ function wireRepairFetch(mc, opts) {
|
|
|
28123
31212
|
}
|
|
28124
31213
|
return () => {
|
|
28125
31214
|
disposed = true;
|
|
31215
|
+
mc.setUnicastRepair?.(false);
|
|
28126
31216
|
removeResetObserver();
|
|
28127
31217
|
removeLifecycleObserver();
|
|
28128
31218
|
for (const entry of [...entries.values()]) {
|
|
@@ -28332,4 +31422,16 @@ export {
|
|
|
28332
31422
|
wireRepairFetch,
|
|
28333
31423
|
wrapAdtsFrame
|
|
28334
31424
|
};
|
|
31425
|
+
/*! Bundled license information:
|
|
31426
|
+
|
|
31427
|
+
@noble/hashes/esm/utils.js:
|
|
31428
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
31429
|
+
|
|
31430
|
+
@noble/curves/esm/utils.js:
|
|
31431
|
+
@noble/curves/esm/abstract/modular.js:
|
|
31432
|
+
@noble/curves/esm/abstract/curve.js:
|
|
31433
|
+
@noble/curves/esm/abstract/edwards.js:
|
|
31434
|
+
@noble/curves/esm/ed25519.js:
|
|
31435
|
+
(*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
31436
|
+
*/
|
|
28335
31437
|
//# sourceMappingURL=browser-runtime.js.map
|