@did-btcr2/method 0.36.0 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +316 -169
- package/dist/browser.mjs +316 -169
- package/dist/cjs/index.js +317 -170
- package/dist/esm/core/aggregation/participant.js +11 -0
- package/dist/esm/core/aggregation/participant.js.map +1 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js +37 -2
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +318 -192
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
- package/dist/types/core/aggregation/participant.d.ts +9 -0
- package/dist/types/core/aggregation/participant.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/events.d.ts +22 -11
- package/dist/types/core/aggregation/runner/events.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/participant-runner.d.ts +21 -12
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts +76 -22
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/aggregation/participant.ts +12 -0
- package/src/core/aggregation/runner/events.ts +23 -14
- package/src/core/aggregation/runner/participant-runner.ts +43 -13
- package/src/core/aggregation/runner/service-runner.ts +370 -199
package/dist/browser.js
CHANGED
|
@@ -104727,6 +104727,17 @@ a=end-of-candidates
|
|
|
104727
104727
|
}
|
|
104728
104728
|
return map3;
|
|
104729
104729
|
}
|
|
104730
|
+
/**
|
|
104731
|
+
* The validated aggregated data retained for a cohort, regardless of phase.
|
|
104732
|
+
* Unlike {@link pendingValidations} (which lists only cohorts still awaiting
|
|
104733
|
+
* the validate decision), this returns the stored validation — including the
|
|
104734
|
+
* participant's sidecar (the CAS Announcement map or its SMT inclusion proof)
|
|
104735
|
+
* — so it is still readable once the cohort reaches Complete. Returns
|
|
104736
|
+
* undefined before aggregated data has been received.
|
|
104737
|
+
*/
|
|
104738
|
+
getValidation(cohortId) {
|
|
104739
|
+
return this.#cohortStates.get(cohortId)?.validation;
|
|
104740
|
+
}
|
|
104730
104741
|
#handleDistributeAggregatedData(message2) {
|
|
104731
104742
|
const cohortId = message2.body?.cohortId;
|
|
104732
104743
|
if (!cohortId) return;
|
|
@@ -111603,35 +111614,22 @@ ${value2}`;
|
|
|
111603
111614
|
session;
|
|
111604
111615
|
#transport;
|
|
111605
111616
|
#did;
|
|
111606
|
-
#
|
|
111617
|
+
#defaultConfig;
|
|
111607
111618
|
#onOptInReceived;
|
|
111608
111619
|
#onReadyToFinalize;
|
|
111609
111620
|
#onProvideTxData;
|
|
111610
111621
|
#cohortTtlMs;
|
|
111611
111622
|
#phaseTimeoutMs;
|
|
111612
111623
|
#advertRepeatIntervalMs;
|
|
111613
|
-
|
|
111624
|
+
/** Per-cohort run state, keyed by cohortId. */
|
|
111625
|
+
#contexts = /* @__PURE__ */ new Map();
|
|
111614
111626
|
#handlersRegistered = false;
|
|
111615
111627
|
#stopped = false;
|
|
111616
|
-
/**
|
|
111617
|
-
* Guard against the async race where two concurrent #handleOptIn invocations
|
|
111618
|
-
* both pass the `participants.length >= minParticipants` check before either
|
|
111619
|
-
* mutates the cohort phase. Set synchronously before any `await` so subsequent
|
|
111620
|
-
* handlers observe it on their next resumption.
|
|
111621
|
-
*/
|
|
111622
|
-
#finalizing = false;
|
|
111623
|
-
#resolveRun;
|
|
111624
|
-
#rejectRun;
|
|
111625
|
-
#cohortTtlTimer;
|
|
111626
|
-
#phaseTimer;
|
|
111627
|
-
#lastObservedPhase;
|
|
111628
|
-
/** Stop handle for the repeating COHORT_ADVERT publish loop. */
|
|
111629
|
-
#stopAdvertRepeat;
|
|
111630
111628
|
constructor(options2) {
|
|
111631
111629
|
super();
|
|
111632
111630
|
this.#transport = options2.transport;
|
|
111633
111631
|
this.#did = options2.did;
|
|
111634
|
-
this.#
|
|
111632
|
+
this.#defaultConfig = options2.config;
|
|
111635
111633
|
this.#onOptInReceived = options2.onOptInReceived ?? (async () => ({ accepted: true }));
|
|
111636
111634
|
this.#onReadyToFinalize = options2.onReadyToFinalize ?? (async ({ acceptedCount, minRequired }) => ({
|
|
111637
111635
|
finalize: acceptedCount >= minRequired
|
|
@@ -111649,55 +111647,126 @@ ${value2}`;
|
|
|
111649
111647
|
maxUpdateSizeBytes: options2.maxUpdateSizeBytes
|
|
111650
111648
|
});
|
|
111651
111649
|
}
|
|
111650
|
+
/** Resolve the {@link RunContext} an inbound message belongs to, by cohortId. */
|
|
111651
|
+
#contextFor(msg) {
|
|
111652
|
+
const cohortId = msg.body?.cohortId;
|
|
111653
|
+
if (!cohortId) return void 0;
|
|
111654
|
+
return this.#contexts.get(cohortId);
|
|
111655
|
+
}
|
|
111652
111656
|
/**
|
|
111653
|
-
* Drain any silent rejections the state machine recorded
|
|
111654
|
-
* recent receive() and surface them as `message-rejected` events.
|
|
111655
|
-
* call even before a cohortId is assigned.
|
|
111657
|
+
* Drain any silent rejections the state machine recorded for a cohort during
|
|
111658
|
+
* the most recent receive() and surface them as `message-rejected` events.
|
|
111656
111659
|
*/
|
|
111657
|
-
#drainRejections() {
|
|
111658
|
-
|
|
111659
|
-
|
|
111660
|
-
this.emit("message-rejected", { cohortId: this.#cohortId, ...r2 });
|
|
111660
|
+
#drainRejections(ctx) {
|
|
111661
|
+
for (const r2 of this.session.drainRejections(ctx.cohortId)) {
|
|
111662
|
+
this.emit("message-rejected", { cohortId: ctx.cohortId, ...r2 });
|
|
111661
111663
|
}
|
|
111662
111664
|
}
|
|
111663
111665
|
/**
|
|
111664
|
-
*
|
|
111665
|
-
*
|
|
111666
|
+
* Advertise a new cohort and begin driving it to completion. Callable many
|
|
111667
|
+
* times on one runner; each cohort runs concurrently and independently.
|
|
111668
|
+
*
|
|
111669
|
+
* @param config Per-cohort conditions + network (see {@link CohortConfig}).
|
|
111670
|
+
* @returns The new cohort's id and a `completion` promise that resolves with
|
|
111671
|
+
* that cohort's {@link AggregationResult} (or rejects if it fails/stalls).
|
|
111672
|
+
* @throws If the runner has been stopped, or the config is invalid
|
|
111673
|
+
* (fail-fast via `createCohort`).
|
|
111674
|
+
*/
|
|
111675
|
+
advertiseCohort(config) {
|
|
111676
|
+
if (this.#stopped) {
|
|
111677
|
+
throw new AggregationServiceError("Cannot advertise on a stopped runner.", "RUNNER_STOPPED", {});
|
|
111678
|
+
}
|
|
111679
|
+
this.#registerHandlers();
|
|
111680
|
+
const cohortId = this.session.createCohort(config);
|
|
111681
|
+
let resolve;
|
|
111682
|
+
let reject;
|
|
111683
|
+
const completion = new Promise((res, rej) => {
|
|
111684
|
+
resolve = res;
|
|
111685
|
+
reject = rej;
|
|
111686
|
+
});
|
|
111687
|
+
const ctx = {
|
|
111688
|
+
cohortId,
|
|
111689
|
+
config,
|
|
111690
|
+
resolve,
|
|
111691
|
+
reject,
|
|
111692
|
+
completion,
|
|
111693
|
+
finalizing: false,
|
|
111694
|
+
settled: false
|
|
111695
|
+
};
|
|
111696
|
+
this.#contexts.set(cohortId, ctx);
|
|
111697
|
+
try {
|
|
111698
|
+
this.#startTimers(ctx);
|
|
111699
|
+
const advertMsgs = this.session.advertise(cohortId);
|
|
111700
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111701
|
+
this.emit("cohort-advertised", { cohortId });
|
|
111702
|
+
if (this.#advertRepeatIntervalMs > 0) {
|
|
111703
|
+
this.#startAdvertRepeat(ctx, advertMsgs);
|
|
111704
|
+
} else {
|
|
111705
|
+
this.#sendAll(advertMsgs).catch((err) => this.#failCohort(ctx, err));
|
|
111706
|
+
}
|
|
111707
|
+
} catch (err) {
|
|
111708
|
+
this.#failCohort(ctx, err);
|
|
111709
|
+
}
|
|
111710
|
+
return { cohortId, completion };
|
|
111711
|
+
}
|
|
111712
|
+
/**
|
|
111713
|
+
* Run a single cohort to completion using the `config` supplied in the
|
|
111714
|
+
* runner options. Thin convenience over {@link advertiseCohort} for the
|
|
111715
|
+
* single-cohort case (and the path {@link AggregationRunner.solo} rides).
|
|
111666
111716
|
*
|
|
111667
111717
|
* @returns {Promise<AggregationResult>} The final result with signature and signed tx.
|
|
111668
111718
|
*/
|
|
111669
111719
|
run() {
|
|
111670
|
-
|
|
111671
|
-
|
|
111672
|
-
|
|
111673
|
-
|
|
111674
|
-
|
|
111675
|
-
|
|
111676
|
-
|
|
111677
|
-
|
|
111678
|
-
|
|
111679
|
-
|
|
111680
|
-
|
|
111681
|
-
|
|
111682
|
-
|
|
111683
|
-
|
|
111684
|
-
|
|
111685
|
-
|
|
111686
|
-
|
|
111720
|
+
if (!this.#defaultConfig) {
|
|
111721
|
+
return Promise.reject(new AggregationServiceError(
|
|
111722
|
+
"run() requires `config` in the runner options; use advertiseCohort(config) to drive cohorts explicitly.",
|
|
111723
|
+
"MISSING_COHORT_CONFIG",
|
|
111724
|
+
{}
|
|
111725
|
+
));
|
|
111726
|
+
}
|
|
111727
|
+
try {
|
|
111728
|
+
return this.advertiseCohort(this.#defaultConfig).completion;
|
|
111729
|
+
} catch (err) {
|
|
111730
|
+
return Promise.reject(err);
|
|
111731
|
+
}
|
|
111732
|
+
}
|
|
111733
|
+
/**
|
|
111734
|
+
* Wait for every currently-outstanding cohort to settle and return the
|
|
111735
|
+
* successful results. Dynamic drain: cohorts advertised while this is pending
|
|
111736
|
+
* are included, and it resolves only once no cohorts remain. Failed cohorts
|
|
111737
|
+
* are surfaced via `error` / `cohort-failed` events and their rejected
|
|
111738
|
+
* `completion` promises; they are omitted from the returned array (this
|
|
111739
|
+
* method does not throw). Bound long-running cohorts with `cohortTtlMs` /
|
|
111740
|
+
* `phaseTimeoutMs` or this may never resolve.
|
|
111741
|
+
*
|
|
111742
|
+
* @returns {Promise<AggregationResult[]>} Results of the cohorts that completed.
|
|
111743
|
+
*/
|
|
111744
|
+
async runAll() {
|
|
111745
|
+
const collected = /* @__PURE__ */ new Map();
|
|
111746
|
+
const onComplete = (result) => {
|
|
111747
|
+
collected.set(result.cohortId, result);
|
|
111748
|
+
};
|
|
111749
|
+
this.on("signing-complete", onComplete);
|
|
111750
|
+
try {
|
|
111751
|
+
while (this.#contexts.size > 0) {
|
|
111752
|
+
await Promise.allSettled([...this.#contexts.values()].map((c2) => c2.completion));
|
|
111687
111753
|
}
|
|
111688
|
-
}
|
|
111754
|
+
} finally {
|
|
111755
|
+
this.off("signing-complete", onComplete);
|
|
111756
|
+
}
|
|
111757
|
+
return [...collected.values()];
|
|
111689
111758
|
}
|
|
111690
111759
|
/**
|
|
111691
|
-
* Begin publishing
|
|
111692
|
-
* until
|
|
111693
|
-
*
|
|
111760
|
+
* Begin publishing a cohort's advert immediately and on a repeating interval
|
|
111761
|
+
* until the cohort's advert loop is stopped. Each advert is broadcast (no
|
|
111762
|
+
* recipient) via the transport's `publishRepeating` primitive.
|
|
111694
111763
|
*/
|
|
111695
|
-
#startAdvertRepeat(advertMsgs) {
|
|
111764
|
+
#startAdvertRepeat(ctx, advertMsgs) {
|
|
111696
111765
|
const stops = [];
|
|
111697
111766
|
for (const msg of advertMsgs) {
|
|
111698
111767
|
stops.push(this.#transport.publishRepeating(msg, this.#did, this.#advertRepeatIntervalMs));
|
|
111699
111768
|
}
|
|
111700
|
-
|
|
111769
|
+
ctx.stopAdvertRepeat = () => {
|
|
111701
111770
|
for (const stop2 of stops) {
|
|
111702
111771
|
try {
|
|
111703
111772
|
stop2();
|
|
@@ -111706,61 +111775,118 @@ ${value2}`;
|
|
|
111706
111775
|
}
|
|
111707
111776
|
};
|
|
111708
111777
|
}
|
|
111709
|
-
/** Stop
|
|
111710
|
-
#stopAdvertRepeating() {
|
|
111711
|
-
if (!
|
|
111712
|
-
const stop2 =
|
|
111713
|
-
|
|
111778
|
+
/** Stop a cohort's advert republish loop. Idempotent. */
|
|
111779
|
+
#stopAdvertRepeating(ctx) {
|
|
111780
|
+
if (!ctx.stopAdvertRepeat) return;
|
|
111781
|
+
const stop2 = ctx.stopAdvertRepeat;
|
|
111782
|
+
ctx.stopAdvertRepeat = void 0;
|
|
111714
111783
|
stop2();
|
|
111715
111784
|
}
|
|
111716
|
-
/** Schedule cohort TTL + phase timeout
|
|
111717
|
-
#startTimers() {
|
|
111785
|
+
/** Schedule a cohort's TTL + phase timeout when it is advertised. */
|
|
111786
|
+
#startTimers(ctx) {
|
|
111718
111787
|
if (this.#cohortTtlMs !== void 0) {
|
|
111719
|
-
|
|
111720
|
-
const reason = `Cohort ${
|
|
111721
|
-
this.emit("cohort-failed", { cohortId:
|
|
111722
|
-
this.#
|
|
111788
|
+
ctx.cohortTtlTimer = setTimeout(() => {
|
|
111789
|
+
const reason = `Cohort ${ctx.cohortId} exceeded TTL of ${this.#cohortTtlMs}ms`;
|
|
111790
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111791
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111723
111792
|
}, this.#cohortTtlMs);
|
|
111724
111793
|
}
|
|
111725
|
-
this.#resetPhaseTimer();
|
|
111794
|
+
this.#resetPhaseTimer(ctx);
|
|
111726
111795
|
}
|
|
111727
|
-
/** Reset
|
|
111728
|
-
#resetPhaseTimer() {
|
|
111729
|
-
if (
|
|
111730
|
-
|
|
111796
|
+
/** Reset a cohort's per-phase stall timer. Called when a phase transition is observed. */
|
|
111797
|
+
#resetPhaseTimer(ctx) {
|
|
111798
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111799
|
+
ctx.phaseTimer = void 0;
|
|
111731
111800
|
if (this.#phaseTimeoutMs === void 0) return;
|
|
111732
|
-
|
|
111733
|
-
const reason = `Cohort ${
|
|
111734
|
-
this.emit("cohort-failed", { cohortId:
|
|
111735
|
-
this.#
|
|
111801
|
+
ctx.phaseTimer = setTimeout(() => {
|
|
111802
|
+
const reason = `Cohort ${ctx.cohortId} stalled in phase ${ctx.lastObservedPhase ?? "?"} for ${this.#phaseTimeoutMs}ms`;
|
|
111803
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111804
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111736
111805
|
}, this.#phaseTimeoutMs);
|
|
111737
111806
|
}
|
|
111738
|
-
/** Detect a phase change since the last observation and reset
|
|
111739
|
-
#onPhaseMaybeChanged() {
|
|
111740
|
-
|
|
111741
|
-
|
|
111742
|
-
|
|
111743
|
-
this.#
|
|
111744
|
-
this.#resetPhaseTimer();
|
|
111807
|
+
/** Detect a phase change for a cohort since the last observation and reset its phase timer. */
|
|
111808
|
+
#onPhaseMaybeChanged(ctx) {
|
|
111809
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111810
|
+
if (phase !== ctx.lastObservedPhase) {
|
|
111811
|
+
ctx.lastObservedPhase = phase;
|
|
111812
|
+
this.#resetPhaseTimer(ctx);
|
|
111745
111813
|
}
|
|
111746
111814
|
}
|
|
111747
|
-
/** Clear
|
|
111748
|
-
#clearTimers() {
|
|
111749
|
-
if (
|
|
111750
|
-
if (
|
|
111751
|
-
|
|
111752
|
-
|
|
111815
|
+
/** Clear a cohort's timers. Called on completion, stop, and failure. */
|
|
111816
|
+
#clearTimers(ctx) {
|
|
111817
|
+
if (ctx.cohortTtlTimer) clearTimeout(ctx.cohortTtlTimer);
|
|
111818
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111819
|
+
ctx.cohortTtlTimer = void 0;
|
|
111820
|
+
ctx.phaseTimer = void 0;
|
|
111821
|
+
}
|
|
111822
|
+
/**
|
|
111823
|
+
* Reclaim one cohort's runner-layer bookkeeping: stop its advert loop, clear
|
|
111824
|
+
* its timers, and drop its {@link RunContext}. Does NOT touch sibling cohorts
|
|
111825
|
+
* and does NOT detach the shared transport handlers. Leaves the cohort in the
|
|
111826
|
+
* state machine; whether that cohort's `session` state is also removed is the
|
|
111827
|
+
* caller's choice (see {@link #completeCohort} vs {@link #failCohort}).
|
|
111828
|
+
*/
|
|
111829
|
+
#disposeCohort(ctx) {
|
|
111830
|
+
this.#stopAdvertRepeating(ctx);
|
|
111831
|
+
this.#clearTimers(ctx);
|
|
111832
|
+
this.#contexts.delete(ctx.cohortId);
|
|
111833
|
+
}
|
|
111834
|
+
/**
|
|
111835
|
+
* Settle one cohort successfully. Reclaims the runner context but leaves the
|
|
111836
|
+
* completed cohort in `session` so callers can read its beaconAddress / cohort
|
|
111837
|
+
* via `session.getCohort(result.cohortId)`; reclaim it with
|
|
111838
|
+
* `session.removeCohort(cohortId)` when done. Idempotent via `ctx.settled`.
|
|
111839
|
+
*/
|
|
111840
|
+
#completeCohort(ctx, result) {
|
|
111841
|
+
if (ctx.settled) return;
|
|
111842
|
+
ctx.settled = true;
|
|
111843
|
+
this.#disposeCohort(ctx);
|
|
111844
|
+
this.emit("signing-complete", result);
|
|
111845
|
+
ctx.resolve(result);
|
|
111846
|
+
}
|
|
111847
|
+
/**
|
|
111848
|
+
* Fail one cohort. Reclaims its runner context, drops its now-dead state from
|
|
111849
|
+
* the state machine, and rejects only its completion; siblings keep running
|
|
111850
|
+
* and the shared transport handlers stay registered. Idempotent via
|
|
111851
|
+
* `ctx.settled`.
|
|
111852
|
+
*/
|
|
111853
|
+
#failCohort(ctx, err) {
|
|
111854
|
+
if (ctx.settled) return;
|
|
111855
|
+
ctx.settled = true;
|
|
111856
|
+
this.#disposeCohort(ctx);
|
|
111857
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111858
|
+
this.emit("error", err);
|
|
111859
|
+
ctx.reject(err);
|
|
111860
|
+
}
|
|
111861
|
+
/**
|
|
111862
|
+
* Stop a single cohort early without affecting the rest of the runner. Drops
|
|
111863
|
+
* the cohort's state machine state; its `completion` promise rejects with a
|
|
111864
|
+
* stopped error.
|
|
111865
|
+
*/
|
|
111866
|
+
stopCohort(cohortId) {
|
|
111867
|
+
const ctx = this.#contexts.get(cohortId);
|
|
111868
|
+
if (!ctx || ctx.settled) return;
|
|
111869
|
+
ctx.settled = true;
|
|
111870
|
+
this.#disposeCohort(ctx);
|
|
111871
|
+
this.session.removeCohort(cohortId);
|
|
111872
|
+
ctx.reject(new AggregationServiceError(`Cohort ${cohortId} stopped.`, "COHORT_STOPPED", { cohortId }));
|
|
111753
111873
|
}
|
|
111754
111874
|
/**
|
|
111755
|
-
* Stop the runner
|
|
111756
|
-
* handlers so a restart or a new runner doesn't inherit
|
|
111875
|
+
* Stop the whole runner. Fails every outstanding cohort, then detaches the
|
|
111876
|
+
* shared transport handlers so a restart or a new runner doesn't inherit
|
|
111877
|
+
* stale dispatch. Safe to call repeatedly.
|
|
111757
111878
|
*/
|
|
111758
111879
|
stop() {
|
|
111759
111880
|
this.#stopped = true;
|
|
111760
|
-
this.#
|
|
111761
|
-
|
|
111881
|
+
for (const ctx of [...this.#contexts.values()]) {
|
|
111882
|
+
if (ctx.settled) continue;
|
|
111883
|
+
ctx.settled = true;
|
|
111884
|
+
this.#disposeCohort(ctx);
|
|
111885
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111886
|
+
ctx.reject(new AggregationServiceError("Service runner stopped.", "RUNNER_STOPPED", { cohortId: ctx.cohortId }));
|
|
111887
|
+
}
|
|
111888
|
+
this.#contexts.clear();
|
|
111762
111889
|
this.#unregisterHandlers();
|
|
111763
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111764
111890
|
}
|
|
111765
111891
|
/** Message types this runner listens for on the transport. */
|
|
111766
111892
|
static #HANDLED_MESSAGE_TYPES = [
|
|
@@ -111771,7 +111897,10 @@ ${value2}`;
|
|
|
111771
111897
|
SIGNATURE_AUTHORIZATION
|
|
111772
111898
|
];
|
|
111773
111899
|
/**
|
|
111774
|
-
* Internal: handler registration with the transport. Idempotent.
|
|
111900
|
+
* Internal: handler registration with the transport. Idempotent. Handlers
|
|
111901
|
+
* are DID-scoped and cohort-agnostic — one registration serves every cohort
|
|
111902
|
+
* this runner drives; demux to the right {@link RunContext} happens in each
|
|
111903
|
+
* handler via the inbound message's cohortId.
|
|
111775
111904
|
*/
|
|
111776
111905
|
#registerHandlers() {
|
|
111777
111906
|
if (this.#handlersRegistered) return;
|
|
@@ -111792,23 +111921,25 @@ ${value2}`;
|
|
|
111792
111921
|
}
|
|
111793
111922
|
/**
|
|
111794
111923
|
* Internal: message handlers for each protocol step. Each handler:
|
|
111795
|
-
* 1)
|
|
111796
|
-
* 2)
|
|
111797
|
-
* 3)
|
|
111924
|
+
* 1) resolves the cohort the message belongs to (by cohortId); ignores it if unknown
|
|
111925
|
+
* 2) feeds the message into the state machine via session.receive()
|
|
111926
|
+
* 3) emits a high-level event (carrying cohortId) for external observers
|
|
111927
|
+
* 4) checks if the new state triggers any automatic next steps, and if so:
|
|
111798
111928
|
* a) calls the appropriate decision callback(s)
|
|
111799
111929
|
* b) sends any resulting messages from the state machine
|
|
111930
|
+
* Errors fail only the owning cohort. A stopped runner ignores messages.
|
|
111800
111931
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111801
111932
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111802
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111803
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111804
111933
|
*/
|
|
111805
111934
|
async #handleOptIn(msg) {
|
|
111806
111935
|
if (this.#stopped) return;
|
|
111936
|
+
const ctx = this.#contextFor(msg);
|
|
111937
|
+
if (!ctx) return;
|
|
111807
111938
|
try {
|
|
111808
111939
|
this.session.receive(msg);
|
|
111809
|
-
this.#drainRejections();
|
|
111810
|
-
this.#onPhaseMaybeChanged();
|
|
111811
|
-
const optIn = this.session.pendingOptIns(
|
|
111940
|
+
this.#drainRejections(ctx);
|
|
111941
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111942
|
+
const optIn = this.session.pendingOptIns(ctx.cohortId).get(msg.from);
|
|
111812
111943
|
if (!optIn) return;
|
|
111813
111944
|
this.emit("opt-in-received", optIn);
|
|
111814
111945
|
if (optIn.communicationPk) {
|
|
@@ -111816,34 +111947,34 @@ ${value2}`;
|
|
|
111816
111947
|
}
|
|
111817
111948
|
const decision = await this.#onOptInReceived(optIn);
|
|
111818
111949
|
if (!decision.accepted) return;
|
|
111819
|
-
const maxParticipants =
|
|
111820
|
-
const cohortNow = this.session.getCohort(
|
|
111950
|
+
const maxParticipants = ctx.config.maxParticipants;
|
|
111951
|
+
const cohortNow = this.session.getCohort(ctx.cohortId);
|
|
111821
111952
|
if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
|
|
111822
111953
|
return;
|
|
111823
111954
|
}
|
|
111824
|
-
await this.#sendAll(this.session.acceptParticipant(
|
|
111825
|
-
this.emit("participant-accepted", { participantDid: msg.from });
|
|
111826
|
-
const cohort = this.session.getCohort(
|
|
111827
|
-
if (cohort.participants.length >=
|
|
111828
|
-
|
|
111955
|
+
await this.#sendAll(this.session.acceptParticipant(ctx.cohortId, msg.from));
|
|
111956
|
+
this.emit("participant-accepted", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111957
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111958
|
+
if (cohort.participants.length >= ctx.config.minParticipants && !ctx.finalizing) {
|
|
111959
|
+
ctx.finalizing = true;
|
|
111829
111960
|
const finalizeDecision = await this.#onReadyToFinalize({
|
|
111830
111961
|
acceptedCount: cohort.participants.length,
|
|
111831
|
-
minRequired:
|
|
111962
|
+
minRequired: ctx.config.minParticipants
|
|
111832
111963
|
});
|
|
111833
111964
|
if (!finalizeDecision.finalize) {
|
|
111834
|
-
|
|
111965
|
+
ctx.finalizing = false;
|
|
111835
111966
|
return;
|
|
111836
111967
|
}
|
|
111837
|
-
const readyMsgs = this.session.finalizeKeygen(
|
|
111838
|
-
this.#stopAdvertRepeating();
|
|
111968
|
+
const readyMsgs = this.session.finalizeKeygen(ctx.cohortId);
|
|
111969
|
+
this.#stopAdvertRepeating(ctx);
|
|
111839
111970
|
this.emit("keygen-complete", {
|
|
111840
|
-
cohortId:
|
|
111971
|
+
cohortId: ctx.cohortId,
|
|
111841
111972
|
beaconAddress: cohort.beaconAddress
|
|
111842
111973
|
});
|
|
111843
111974
|
await this.#sendAll(readyMsgs);
|
|
111844
111975
|
}
|
|
111845
111976
|
} catch (err) {
|
|
111846
|
-
this.#
|
|
111977
|
+
this.#failCohort(ctx, err);
|
|
111847
111978
|
}
|
|
111848
111979
|
}
|
|
111849
111980
|
/**
|
|
@@ -111851,23 +111982,23 @@ ${value2}`;
|
|
|
111851
111982
|
* and distributes the data for validation.
|
|
111852
111983
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111853
111984
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111854
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111855
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111856
111985
|
*/
|
|
111857
111986
|
async #handleSubmitUpdate(msg) {
|
|
111858
111987
|
if (this.#stopped) return;
|
|
111988
|
+
const ctx = this.#contextFor(msg);
|
|
111989
|
+
if (!ctx) return;
|
|
111859
111990
|
try {
|
|
111860
111991
|
this.session.receive(msg);
|
|
111861
|
-
this.#drainRejections();
|
|
111862
|
-
this.#onPhaseMaybeChanged();
|
|
111863
|
-
this.emit("update-received", { participantDid: msg.from });
|
|
111864
|
-
if (this.session.getCohortPhase(
|
|
111865
|
-
const distributeMsgs = this.session.buildAndDistribute(
|
|
111866
|
-
this.emit("data-distributed", { cohortId:
|
|
111992
|
+
this.#drainRejections(ctx);
|
|
111993
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111994
|
+
this.emit("update-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111995
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "UpdatesCollected" /* UpdatesCollected */) {
|
|
111996
|
+
const distributeMsgs = this.session.buildAndDistribute(ctx.cohortId);
|
|
111997
|
+
this.emit("data-distributed", { cohortId: ctx.cohortId });
|
|
111867
111998
|
await this.#sendAll(distributeMsgs);
|
|
111868
111999
|
}
|
|
111869
112000
|
} catch (err) {
|
|
111870
|
-
this.#
|
|
112001
|
+
this.#failCohort(ctx, err);
|
|
111871
112002
|
}
|
|
111872
112003
|
}
|
|
111873
112004
|
/**
|
|
@@ -111875,111 +112006,95 @@ ${value2}`;
|
|
|
111875
112006
|
* automatically requests tx data and starts signing.
|
|
111876
112007
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111877
112008
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111878
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111879
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111880
112009
|
*/
|
|
111881
112010
|
async #handleValidationAck(msg) {
|
|
111882
112011
|
if (this.#stopped) return;
|
|
112012
|
+
const ctx = this.#contextFor(msg);
|
|
112013
|
+
if (!ctx) return;
|
|
111883
112014
|
try {
|
|
111884
112015
|
this.session.receive(msg);
|
|
111885
|
-
this.#drainRejections();
|
|
111886
|
-
this.#onPhaseMaybeChanged();
|
|
112016
|
+
this.#drainRejections(ctx);
|
|
112017
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111887
112018
|
const approved = !!msg.body?.approved;
|
|
111888
|
-
this.emit("validation-received", { participantDid: msg.from, approved });
|
|
111889
|
-
const phase = this.session.getCohortPhase(
|
|
112019
|
+
this.emit("validation-received", { cohortId: ctx.cohortId, participantDid: msg.from, approved });
|
|
112020
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111890
112021
|
if (phase === "Failed" /* Failed */) {
|
|
111891
112022
|
const reason = `Validation rejected by participant ${msg.from}`;
|
|
111892
|
-
this.emit("cohort-failed", { cohortId:
|
|
111893
|
-
this.#
|
|
112023
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
112024
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111894
112025
|
return;
|
|
111895
112026
|
}
|
|
111896
112027
|
if (phase === "Validated" /* Validated */) {
|
|
111897
|
-
const cohort = this.session.getCohort(
|
|
112028
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111898
112029
|
const txData = await this.#onProvideTxData({
|
|
111899
|
-
cohortId:
|
|
112030
|
+
cohortId: ctx.cohortId,
|
|
111900
112031
|
beaconAddress: cohort.beaconAddress,
|
|
111901
112032
|
signalBytes: cohort.signalBytes
|
|
111902
112033
|
});
|
|
111903
|
-
const authMsgs = this.session.startSigning(
|
|
111904
|
-
const sessionId = this.session.getSigningSessionId(
|
|
111905
|
-
this.emit("signing-started", { sessionId });
|
|
112034
|
+
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
112035
|
+
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
112036
|
+
this.emit("signing-started", { cohortId: ctx.cohortId, sessionId });
|
|
111906
112037
|
await this.#sendAll(authMsgs);
|
|
111907
112038
|
}
|
|
111908
112039
|
} catch (err) {
|
|
111909
|
-
this.#
|
|
112040
|
+
this.#failCohort(ctx, err);
|
|
111910
112041
|
}
|
|
111911
112042
|
}
|
|
111912
112043
|
/**
|
|
111913
|
-
* Handler for receiving nonce contributions
|
|
111914
|
-
*
|
|
112044
|
+
* Handler for receiving nonce contributions. When all nonces are received, sends the aggregated
|
|
112045
|
+
* nonce back to the cohort.
|
|
111915
112046
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111916
112047
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111917
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111918
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111919
112048
|
*/
|
|
111920
112049
|
async #handleNonceContribution(msg) {
|
|
111921
112050
|
if (this.#stopped) return;
|
|
112051
|
+
const ctx = this.#contextFor(msg);
|
|
112052
|
+
if (!ctx) return;
|
|
111922
112053
|
try {
|
|
111923
112054
|
this.session.receive(msg);
|
|
111924
|
-
this.#drainRejections();
|
|
111925
|
-
this.#onPhaseMaybeChanged();
|
|
111926
|
-
this.emit("nonce-received", { participantDid: msg.from });
|
|
111927
|
-
if (this.session.getCohortPhase(
|
|
111928
|
-
await this.#sendAll(this.session.sendAggregatedNonce(
|
|
112055
|
+
this.#drainRejections(ctx);
|
|
112056
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
112057
|
+
this.emit("nonce-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
112058
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "NoncesCollected" /* NoncesCollected */) {
|
|
112059
|
+
await this.#sendAll(this.session.sendAggregatedNonce(ctx.cohortId));
|
|
111929
112060
|
}
|
|
111930
112061
|
} catch (err) {
|
|
111931
|
-
this.#
|
|
112062
|
+
this.#failCohort(ctx, err);
|
|
111932
112063
|
}
|
|
111933
112064
|
}
|
|
111934
112065
|
/**
|
|
111935
112066
|
* Handler for receiving signature authorizations. When all partial signatures are received, the
|
|
111936
|
-
* session automatically completes
|
|
112067
|
+
* session automatically completes; the final result is emitted and the cohort's completion
|
|
112068
|
+
* promise resolves.
|
|
111937
112069
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111938
112070
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111939
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111940
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111941
112071
|
*/
|
|
111942
112072
|
async #handleSignatureAuthorization(msg) {
|
|
111943
112073
|
if (this.#stopped) return;
|
|
112074
|
+
const ctx = this.#contextFor(msg);
|
|
112075
|
+
if (!ctx) return;
|
|
111944
112076
|
try {
|
|
111945
112077
|
this.session.receive(msg);
|
|
111946
|
-
this.#drainRejections();
|
|
111947
|
-
this.#onPhaseMaybeChanged();
|
|
111948
|
-
const result = this.session.getResult(
|
|
112078
|
+
this.#drainRejections(ctx);
|
|
112079
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
112080
|
+
const result = this.session.getResult(ctx.cohortId);
|
|
111949
112081
|
if (result) {
|
|
111950
|
-
this.#
|
|
111951
|
-
this.#unregisterHandlers();
|
|
111952
|
-
this.emit("signing-complete", result);
|
|
111953
|
-
this.#resolveRun?.(result);
|
|
112082
|
+
this.#completeCohort(ctx, result);
|
|
111954
112083
|
}
|
|
111955
112084
|
} catch (err) {
|
|
111956
|
-
this.#
|
|
112085
|
+
this.#failCohort(ctx, err);
|
|
111957
112086
|
}
|
|
111958
112087
|
}
|
|
111959
112088
|
/**
|
|
111960
112089
|
* Internal: helper to send all messages sequentially. Catches and propagates errors.
|
|
111961
112090
|
* @param {BaseMessage[]} msgs - The messages to send.
|
|
111962
112091
|
* @returns {Promise<void>} Resolves when all messages have been sent.
|
|
111963
|
-
* @throws {Error} If sending any message fails, the error is emitted and the run promise is
|
|
111964
|
-
* rejected.
|
|
111965
112092
|
*/
|
|
111966
112093
|
async #sendAll(msgs) {
|
|
111967
112094
|
for (const m2 of msgs) {
|
|
111968
112095
|
await this.#transport.sendMessage(m2, this.#did, m2.to);
|
|
111969
112096
|
}
|
|
111970
112097
|
}
|
|
111971
|
-
/**
|
|
111972
|
-
* Internal: helper to handle errors. Emits an 'error' event and rejects the run promise.
|
|
111973
|
-
* @param {Error} err - The error to handle.
|
|
111974
|
-
*/
|
|
111975
|
-
#fail(err) {
|
|
111976
|
-
this.#stopAdvertRepeating();
|
|
111977
|
-
this.#clearTimers();
|
|
111978
|
-
this.#unregisterHandlers();
|
|
111979
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111980
|
-
this.emit("error", err);
|
|
111981
|
-
this.#rejectRun?.(err);
|
|
111982
|
-
}
|
|
111983
112098
|
};
|
|
111984
112099
|
|
|
111985
112100
|
// src/core/aggregation/runner/participant-runner.ts
|
|
@@ -112040,7 +112155,8 @@ ${value2}`;
|
|
|
112040
112155
|
}
|
|
112041
112156
|
/**
|
|
112042
112157
|
* Single-shot helper: start, join the first cohort that passes `shouldJoin`,
|
|
112043
|
-
* drive it to completion, and resolve. Convenient for tests and demos.
|
|
112158
|
+
* drive it to completion, and resolve. Convenient for tests and demos. The
|
|
112159
|
+
* single-cohort special case of {@link joinMatching} (count = 1).
|
|
112044
112160
|
*/
|
|
112045
112161
|
static async joinFirst(options2) {
|
|
112046
112162
|
return new Promise((resolve, reject) => {
|
|
@@ -112053,6 +112169,37 @@ ${value2}`;
|
|
|
112053
112169
|
runner.start().catch(reject);
|
|
112054
112170
|
});
|
|
112055
112171
|
}
|
|
112172
|
+
/**
|
|
112173
|
+
* Multi-cohort helper: start, join EVERY cohort whose advert passes
|
|
112174
|
+
* `shouldJoin`, drive each to completion in parallel, and resolve once
|
|
112175
|
+
* `count` cohorts have completed (the runner stops at that point). The
|
|
112176
|
+
* N-cohort generalization of {@link joinFirst}, for a participant that joins
|
|
112177
|
+
* several cohorts advertised by one service.
|
|
112178
|
+
*
|
|
112179
|
+
* For an open-ended, long-lived subscriber (no fixed count), construct an
|
|
112180
|
+
* {@link AggregationParticipantRunner} directly, set `shouldJoin`, call
|
|
112181
|
+
* `start()`, and listen for `cohort-complete` — the runner already drives
|
|
112182
|
+
* any number of cohorts concurrently.
|
|
112183
|
+
*
|
|
112184
|
+
* @param options Participant runner options (set `shouldJoin` to select cohorts).
|
|
112185
|
+
* @param count Number of completed cohorts to collect before resolving.
|
|
112186
|
+
* @returns The {@link CohortCompleteInfo} for each completed cohort, in completion order.
|
|
112187
|
+
*/
|
|
112188
|
+
static async joinMatching(options2, count) {
|
|
112189
|
+
return new Promise((resolve, reject) => {
|
|
112190
|
+
const runner = new _AggregationParticipantRunner(options2);
|
|
112191
|
+
const completed = [];
|
|
112192
|
+
runner.on("cohort-complete", (info) => {
|
|
112193
|
+
completed.push(info);
|
|
112194
|
+
if (completed.length >= count) {
|
|
112195
|
+
runner.stop();
|
|
112196
|
+
resolve(completed);
|
|
112197
|
+
}
|
|
112198
|
+
});
|
|
112199
|
+
runner.on("error", reject);
|
|
112200
|
+
runner.start().catch(reject);
|
|
112201
|
+
});
|
|
112202
|
+
}
|
|
112056
112203
|
/**
|
|
112057
112204
|
* Internal: handler registration with the transport. Idempotent and safe to call multiple times,
|
|
112058
112205
|
* but only registers handlers once.
|
|
@@ -112201,7 +112348,7 @@ ${value2}`;
|
|
|
112201
112348
|
if (this.session.getCohortPhase(cohortId) === "Complete" /* Complete */) {
|
|
112202
112349
|
const info = this.session.joinedCohorts.get(cohortId);
|
|
112203
112350
|
if (info) {
|
|
112204
|
-
const validation = this.session.
|
|
112351
|
+
const validation = this.session.getValidation(cohortId);
|
|
112205
112352
|
this.emit("cohort-complete", {
|
|
112206
112353
|
cohortId,
|
|
112207
112354
|
beaconAddress: info.beaconAddress,
|