@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.mjs
CHANGED
|
@@ -104589,6 +104589,17 @@ var AggregationParticipant = class {
|
|
|
104589
104589
|
}
|
|
104590
104590
|
return map3;
|
|
104591
104591
|
}
|
|
104592
|
+
/**
|
|
104593
|
+
* The validated aggregated data retained for a cohort, regardless of phase.
|
|
104594
|
+
* Unlike {@link pendingValidations} (which lists only cohorts still awaiting
|
|
104595
|
+
* the validate decision), this returns the stored validation — including the
|
|
104596
|
+
* participant's sidecar (the CAS Announcement map or its SMT inclusion proof)
|
|
104597
|
+
* — so it is still readable once the cohort reaches Complete. Returns
|
|
104598
|
+
* undefined before aggregated data has been received.
|
|
104599
|
+
*/
|
|
104600
|
+
getValidation(cohortId) {
|
|
104601
|
+
return this.#cohortStates.get(cohortId)?.validation;
|
|
104602
|
+
}
|
|
104592
104603
|
#handleDistributeAggregatedData(message2) {
|
|
104593
104604
|
const cohortId = message2.body?.cohortId;
|
|
104594
104605
|
if (!cohortId) return;
|
|
@@ -111465,35 +111476,22 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111465
111476
|
session;
|
|
111466
111477
|
#transport;
|
|
111467
111478
|
#did;
|
|
111468
|
-
#
|
|
111479
|
+
#defaultConfig;
|
|
111469
111480
|
#onOptInReceived;
|
|
111470
111481
|
#onReadyToFinalize;
|
|
111471
111482
|
#onProvideTxData;
|
|
111472
111483
|
#cohortTtlMs;
|
|
111473
111484
|
#phaseTimeoutMs;
|
|
111474
111485
|
#advertRepeatIntervalMs;
|
|
111475
|
-
|
|
111486
|
+
/** Per-cohort run state, keyed by cohortId. */
|
|
111487
|
+
#contexts = /* @__PURE__ */ new Map();
|
|
111476
111488
|
#handlersRegistered = false;
|
|
111477
111489
|
#stopped = false;
|
|
111478
|
-
/**
|
|
111479
|
-
* Guard against the async race where two concurrent #handleOptIn invocations
|
|
111480
|
-
* both pass the `participants.length >= minParticipants` check before either
|
|
111481
|
-
* mutates the cohort phase. Set synchronously before any `await` so subsequent
|
|
111482
|
-
* handlers observe it on their next resumption.
|
|
111483
|
-
*/
|
|
111484
|
-
#finalizing = false;
|
|
111485
|
-
#resolveRun;
|
|
111486
|
-
#rejectRun;
|
|
111487
|
-
#cohortTtlTimer;
|
|
111488
|
-
#phaseTimer;
|
|
111489
|
-
#lastObservedPhase;
|
|
111490
|
-
/** Stop handle for the repeating COHORT_ADVERT publish loop. */
|
|
111491
|
-
#stopAdvertRepeat;
|
|
111492
111490
|
constructor(options2) {
|
|
111493
111491
|
super();
|
|
111494
111492
|
this.#transport = options2.transport;
|
|
111495
111493
|
this.#did = options2.did;
|
|
111496
|
-
this.#
|
|
111494
|
+
this.#defaultConfig = options2.config;
|
|
111497
111495
|
this.#onOptInReceived = options2.onOptInReceived ?? (async () => ({ accepted: true }));
|
|
111498
111496
|
this.#onReadyToFinalize = options2.onReadyToFinalize ?? (async ({ acceptedCount, minRequired }) => ({
|
|
111499
111497
|
finalize: acceptedCount >= minRequired
|
|
@@ -111511,55 +111509,126 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111511
111509
|
maxUpdateSizeBytes: options2.maxUpdateSizeBytes
|
|
111512
111510
|
});
|
|
111513
111511
|
}
|
|
111512
|
+
/** Resolve the {@link RunContext} an inbound message belongs to, by cohortId. */
|
|
111513
|
+
#contextFor(msg) {
|
|
111514
|
+
const cohortId = msg.body?.cohortId;
|
|
111515
|
+
if (!cohortId) return void 0;
|
|
111516
|
+
return this.#contexts.get(cohortId);
|
|
111517
|
+
}
|
|
111514
111518
|
/**
|
|
111515
|
-
* Drain any silent rejections the state machine recorded
|
|
111516
|
-
* recent receive() and surface them as `message-rejected` events.
|
|
111517
|
-
* call even before a cohortId is assigned.
|
|
111519
|
+
* Drain any silent rejections the state machine recorded for a cohort during
|
|
111520
|
+
* the most recent receive() and surface them as `message-rejected` events.
|
|
111518
111521
|
*/
|
|
111519
|
-
#drainRejections() {
|
|
111520
|
-
|
|
111521
|
-
|
|
111522
|
-
this.emit("message-rejected", { cohortId: this.#cohortId, ...r2 });
|
|
111522
|
+
#drainRejections(ctx) {
|
|
111523
|
+
for (const r2 of this.session.drainRejections(ctx.cohortId)) {
|
|
111524
|
+
this.emit("message-rejected", { cohortId: ctx.cohortId, ...r2 });
|
|
111523
111525
|
}
|
|
111524
111526
|
}
|
|
111525
111527
|
/**
|
|
111526
|
-
*
|
|
111527
|
-
*
|
|
111528
|
+
* Advertise a new cohort and begin driving it to completion. Callable many
|
|
111529
|
+
* times on one runner; each cohort runs concurrently and independently.
|
|
111530
|
+
*
|
|
111531
|
+
* @param config Per-cohort conditions + network (see {@link CohortConfig}).
|
|
111532
|
+
* @returns The new cohort's id and a `completion` promise that resolves with
|
|
111533
|
+
* that cohort's {@link AggregationResult} (or rejects if it fails/stalls).
|
|
111534
|
+
* @throws If the runner has been stopped, or the config is invalid
|
|
111535
|
+
* (fail-fast via `createCohort`).
|
|
111536
|
+
*/
|
|
111537
|
+
advertiseCohort(config) {
|
|
111538
|
+
if (this.#stopped) {
|
|
111539
|
+
throw new AggregationServiceError("Cannot advertise on a stopped runner.", "RUNNER_STOPPED", {});
|
|
111540
|
+
}
|
|
111541
|
+
this.#registerHandlers();
|
|
111542
|
+
const cohortId = this.session.createCohort(config);
|
|
111543
|
+
let resolve;
|
|
111544
|
+
let reject;
|
|
111545
|
+
const completion = new Promise((res, rej) => {
|
|
111546
|
+
resolve = res;
|
|
111547
|
+
reject = rej;
|
|
111548
|
+
});
|
|
111549
|
+
const ctx = {
|
|
111550
|
+
cohortId,
|
|
111551
|
+
config,
|
|
111552
|
+
resolve,
|
|
111553
|
+
reject,
|
|
111554
|
+
completion,
|
|
111555
|
+
finalizing: false,
|
|
111556
|
+
settled: false
|
|
111557
|
+
};
|
|
111558
|
+
this.#contexts.set(cohortId, ctx);
|
|
111559
|
+
try {
|
|
111560
|
+
this.#startTimers(ctx);
|
|
111561
|
+
const advertMsgs = this.session.advertise(cohortId);
|
|
111562
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111563
|
+
this.emit("cohort-advertised", { cohortId });
|
|
111564
|
+
if (this.#advertRepeatIntervalMs > 0) {
|
|
111565
|
+
this.#startAdvertRepeat(ctx, advertMsgs);
|
|
111566
|
+
} else {
|
|
111567
|
+
this.#sendAll(advertMsgs).catch((err) => this.#failCohort(ctx, err));
|
|
111568
|
+
}
|
|
111569
|
+
} catch (err) {
|
|
111570
|
+
this.#failCohort(ctx, err);
|
|
111571
|
+
}
|
|
111572
|
+
return { cohortId, completion };
|
|
111573
|
+
}
|
|
111574
|
+
/**
|
|
111575
|
+
* Run a single cohort to completion using the `config` supplied in the
|
|
111576
|
+
* runner options. Thin convenience over {@link advertiseCohort} for the
|
|
111577
|
+
* single-cohort case (and the path {@link AggregationRunner.solo} rides).
|
|
111528
111578
|
*
|
|
111529
111579
|
* @returns {Promise<AggregationResult>} The final result with signature and signed tx.
|
|
111530
111580
|
*/
|
|
111531
111581
|
run() {
|
|
111532
|
-
|
|
111533
|
-
|
|
111534
|
-
|
|
111535
|
-
|
|
111536
|
-
|
|
111537
|
-
|
|
111538
|
-
|
|
111539
|
-
|
|
111540
|
-
|
|
111541
|
-
|
|
111542
|
-
|
|
111543
|
-
|
|
111544
|
-
|
|
111545
|
-
|
|
111546
|
-
|
|
111547
|
-
|
|
111548
|
-
|
|
111582
|
+
if (!this.#defaultConfig) {
|
|
111583
|
+
return Promise.reject(new AggregationServiceError(
|
|
111584
|
+
"run() requires `config` in the runner options; use advertiseCohort(config) to drive cohorts explicitly.",
|
|
111585
|
+
"MISSING_COHORT_CONFIG",
|
|
111586
|
+
{}
|
|
111587
|
+
));
|
|
111588
|
+
}
|
|
111589
|
+
try {
|
|
111590
|
+
return this.advertiseCohort(this.#defaultConfig).completion;
|
|
111591
|
+
} catch (err) {
|
|
111592
|
+
return Promise.reject(err);
|
|
111593
|
+
}
|
|
111594
|
+
}
|
|
111595
|
+
/**
|
|
111596
|
+
* Wait for every currently-outstanding cohort to settle and return the
|
|
111597
|
+
* successful results. Dynamic drain: cohorts advertised while this is pending
|
|
111598
|
+
* are included, and it resolves only once no cohorts remain. Failed cohorts
|
|
111599
|
+
* are surfaced via `error` / `cohort-failed` events and their rejected
|
|
111600
|
+
* `completion` promises; they are omitted from the returned array (this
|
|
111601
|
+
* method does not throw). Bound long-running cohorts with `cohortTtlMs` /
|
|
111602
|
+
* `phaseTimeoutMs` or this may never resolve.
|
|
111603
|
+
*
|
|
111604
|
+
* @returns {Promise<AggregationResult[]>} Results of the cohorts that completed.
|
|
111605
|
+
*/
|
|
111606
|
+
async runAll() {
|
|
111607
|
+
const collected = /* @__PURE__ */ new Map();
|
|
111608
|
+
const onComplete = (result) => {
|
|
111609
|
+
collected.set(result.cohortId, result);
|
|
111610
|
+
};
|
|
111611
|
+
this.on("signing-complete", onComplete);
|
|
111612
|
+
try {
|
|
111613
|
+
while (this.#contexts.size > 0) {
|
|
111614
|
+
await Promise.allSettled([...this.#contexts.values()].map((c2) => c2.completion));
|
|
111549
111615
|
}
|
|
111550
|
-
}
|
|
111616
|
+
} finally {
|
|
111617
|
+
this.off("signing-complete", onComplete);
|
|
111618
|
+
}
|
|
111619
|
+
return [...collected.values()];
|
|
111551
111620
|
}
|
|
111552
111621
|
/**
|
|
111553
|
-
* Begin publishing
|
|
111554
|
-
* until
|
|
111555
|
-
*
|
|
111622
|
+
* Begin publishing a cohort's advert immediately and on a repeating interval
|
|
111623
|
+
* until the cohort's advert loop is stopped. Each advert is broadcast (no
|
|
111624
|
+
* recipient) via the transport's `publishRepeating` primitive.
|
|
111556
111625
|
*/
|
|
111557
|
-
#startAdvertRepeat(advertMsgs) {
|
|
111626
|
+
#startAdvertRepeat(ctx, advertMsgs) {
|
|
111558
111627
|
const stops = [];
|
|
111559
111628
|
for (const msg of advertMsgs) {
|
|
111560
111629
|
stops.push(this.#transport.publishRepeating(msg, this.#did, this.#advertRepeatIntervalMs));
|
|
111561
111630
|
}
|
|
111562
|
-
|
|
111631
|
+
ctx.stopAdvertRepeat = () => {
|
|
111563
111632
|
for (const stop2 of stops) {
|
|
111564
111633
|
try {
|
|
111565
111634
|
stop2();
|
|
@@ -111568,61 +111637,118 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111568
111637
|
}
|
|
111569
111638
|
};
|
|
111570
111639
|
}
|
|
111571
|
-
/** Stop
|
|
111572
|
-
#stopAdvertRepeating() {
|
|
111573
|
-
if (!
|
|
111574
|
-
const stop2 =
|
|
111575
|
-
|
|
111640
|
+
/** Stop a cohort's advert republish loop. Idempotent. */
|
|
111641
|
+
#stopAdvertRepeating(ctx) {
|
|
111642
|
+
if (!ctx.stopAdvertRepeat) return;
|
|
111643
|
+
const stop2 = ctx.stopAdvertRepeat;
|
|
111644
|
+
ctx.stopAdvertRepeat = void 0;
|
|
111576
111645
|
stop2();
|
|
111577
111646
|
}
|
|
111578
|
-
/** Schedule cohort TTL + phase timeout
|
|
111579
|
-
#startTimers() {
|
|
111647
|
+
/** Schedule a cohort's TTL + phase timeout when it is advertised. */
|
|
111648
|
+
#startTimers(ctx) {
|
|
111580
111649
|
if (this.#cohortTtlMs !== void 0) {
|
|
111581
|
-
|
|
111582
|
-
const reason = `Cohort ${
|
|
111583
|
-
this.emit("cohort-failed", { cohortId:
|
|
111584
|
-
this.#
|
|
111650
|
+
ctx.cohortTtlTimer = setTimeout(() => {
|
|
111651
|
+
const reason = `Cohort ${ctx.cohortId} exceeded TTL of ${this.#cohortTtlMs}ms`;
|
|
111652
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111653
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111585
111654
|
}, this.#cohortTtlMs);
|
|
111586
111655
|
}
|
|
111587
|
-
this.#resetPhaseTimer();
|
|
111656
|
+
this.#resetPhaseTimer(ctx);
|
|
111588
111657
|
}
|
|
111589
|
-
/** Reset
|
|
111590
|
-
#resetPhaseTimer() {
|
|
111591
|
-
if (
|
|
111592
|
-
|
|
111658
|
+
/** Reset a cohort's per-phase stall timer. Called when a phase transition is observed. */
|
|
111659
|
+
#resetPhaseTimer(ctx) {
|
|
111660
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111661
|
+
ctx.phaseTimer = void 0;
|
|
111593
111662
|
if (this.#phaseTimeoutMs === void 0) return;
|
|
111594
|
-
|
|
111595
|
-
const reason = `Cohort ${
|
|
111596
|
-
this.emit("cohort-failed", { cohortId:
|
|
111597
|
-
this.#
|
|
111663
|
+
ctx.phaseTimer = setTimeout(() => {
|
|
111664
|
+
const reason = `Cohort ${ctx.cohortId} stalled in phase ${ctx.lastObservedPhase ?? "?"} for ${this.#phaseTimeoutMs}ms`;
|
|
111665
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111666
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111598
111667
|
}, this.#phaseTimeoutMs);
|
|
111599
111668
|
}
|
|
111600
|
-
/** Detect a phase change since the last observation and reset
|
|
111601
|
-
#onPhaseMaybeChanged() {
|
|
111602
|
-
|
|
111603
|
-
|
|
111604
|
-
|
|
111605
|
-
this.#
|
|
111606
|
-
this.#resetPhaseTimer();
|
|
111669
|
+
/** Detect a phase change for a cohort since the last observation and reset its phase timer. */
|
|
111670
|
+
#onPhaseMaybeChanged(ctx) {
|
|
111671
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111672
|
+
if (phase !== ctx.lastObservedPhase) {
|
|
111673
|
+
ctx.lastObservedPhase = phase;
|
|
111674
|
+
this.#resetPhaseTimer(ctx);
|
|
111607
111675
|
}
|
|
111608
111676
|
}
|
|
111609
|
-
/** Clear
|
|
111610
|
-
#clearTimers() {
|
|
111611
|
-
if (
|
|
111612
|
-
if (
|
|
111613
|
-
|
|
111614
|
-
|
|
111677
|
+
/** Clear a cohort's timers. Called on completion, stop, and failure. */
|
|
111678
|
+
#clearTimers(ctx) {
|
|
111679
|
+
if (ctx.cohortTtlTimer) clearTimeout(ctx.cohortTtlTimer);
|
|
111680
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111681
|
+
ctx.cohortTtlTimer = void 0;
|
|
111682
|
+
ctx.phaseTimer = void 0;
|
|
111683
|
+
}
|
|
111684
|
+
/**
|
|
111685
|
+
* Reclaim one cohort's runner-layer bookkeeping: stop its advert loop, clear
|
|
111686
|
+
* its timers, and drop its {@link RunContext}. Does NOT touch sibling cohorts
|
|
111687
|
+
* and does NOT detach the shared transport handlers. Leaves the cohort in the
|
|
111688
|
+
* state machine; whether that cohort's `session` state is also removed is the
|
|
111689
|
+
* caller's choice (see {@link #completeCohort} vs {@link #failCohort}).
|
|
111690
|
+
*/
|
|
111691
|
+
#disposeCohort(ctx) {
|
|
111692
|
+
this.#stopAdvertRepeating(ctx);
|
|
111693
|
+
this.#clearTimers(ctx);
|
|
111694
|
+
this.#contexts.delete(ctx.cohortId);
|
|
111695
|
+
}
|
|
111696
|
+
/**
|
|
111697
|
+
* Settle one cohort successfully. Reclaims the runner context but leaves the
|
|
111698
|
+
* completed cohort in `session` so callers can read its beaconAddress / cohort
|
|
111699
|
+
* via `session.getCohort(result.cohortId)`; reclaim it with
|
|
111700
|
+
* `session.removeCohort(cohortId)` when done. Idempotent via `ctx.settled`.
|
|
111701
|
+
*/
|
|
111702
|
+
#completeCohort(ctx, result) {
|
|
111703
|
+
if (ctx.settled) return;
|
|
111704
|
+
ctx.settled = true;
|
|
111705
|
+
this.#disposeCohort(ctx);
|
|
111706
|
+
this.emit("signing-complete", result);
|
|
111707
|
+
ctx.resolve(result);
|
|
111708
|
+
}
|
|
111709
|
+
/**
|
|
111710
|
+
* Fail one cohort. Reclaims its runner context, drops its now-dead state from
|
|
111711
|
+
* the state machine, and rejects only its completion; siblings keep running
|
|
111712
|
+
* and the shared transport handlers stay registered. Idempotent via
|
|
111713
|
+
* `ctx.settled`.
|
|
111714
|
+
*/
|
|
111715
|
+
#failCohort(ctx, err) {
|
|
111716
|
+
if (ctx.settled) return;
|
|
111717
|
+
ctx.settled = true;
|
|
111718
|
+
this.#disposeCohort(ctx);
|
|
111719
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111720
|
+
this.emit("error", err);
|
|
111721
|
+
ctx.reject(err);
|
|
111722
|
+
}
|
|
111723
|
+
/**
|
|
111724
|
+
* Stop a single cohort early without affecting the rest of the runner. Drops
|
|
111725
|
+
* the cohort's state machine state; its `completion` promise rejects with a
|
|
111726
|
+
* stopped error.
|
|
111727
|
+
*/
|
|
111728
|
+
stopCohort(cohortId) {
|
|
111729
|
+
const ctx = this.#contexts.get(cohortId);
|
|
111730
|
+
if (!ctx || ctx.settled) return;
|
|
111731
|
+
ctx.settled = true;
|
|
111732
|
+
this.#disposeCohort(ctx);
|
|
111733
|
+
this.session.removeCohort(cohortId);
|
|
111734
|
+
ctx.reject(new AggregationServiceError(`Cohort ${cohortId} stopped.`, "COHORT_STOPPED", { cohortId }));
|
|
111615
111735
|
}
|
|
111616
111736
|
/**
|
|
111617
|
-
* Stop the runner
|
|
111618
|
-
* handlers so a restart or a new runner doesn't inherit
|
|
111737
|
+
* Stop the whole runner. Fails every outstanding cohort, then detaches the
|
|
111738
|
+
* shared transport handlers so a restart or a new runner doesn't inherit
|
|
111739
|
+
* stale dispatch. Safe to call repeatedly.
|
|
111619
111740
|
*/
|
|
111620
111741
|
stop() {
|
|
111621
111742
|
this.#stopped = true;
|
|
111622
|
-
this.#
|
|
111623
|
-
|
|
111743
|
+
for (const ctx of [...this.#contexts.values()]) {
|
|
111744
|
+
if (ctx.settled) continue;
|
|
111745
|
+
ctx.settled = true;
|
|
111746
|
+
this.#disposeCohort(ctx);
|
|
111747
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111748
|
+
ctx.reject(new AggregationServiceError("Service runner stopped.", "RUNNER_STOPPED", { cohortId: ctx.cohortId }));
|
|
111749
|
+
}
|
|
111750
|
+
this.#contexts.clear();
|
|
111624
111751
|
this.#unregisterHandlers();
|
|
111625
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111626
111752
|
}
|
|
111627
111753
|
/** Message types this runner listens for on the transport. */
|
|
111628
111754
|
static #HANDLED_MESSAGE_TYPES = [
|
|
@@ -111633,7 +111759,10 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111633
111759
|
SIGNATURE_AUTHORIZATION
|
|
111634
111760
|
];
|
|
111635
111761
|
/**
|
|
111636
|
-
* Internal: handler registration with the transport. Idempotent.
|
|
111762
|
+
* Internal: handler registration with the transport. Idempotent. Handlers
|
|
111763
|
+
* are DID-scoped and cohort-agnostic — one registration serves every cohort
|
|
111764
|
+
* this runner drives; demux to the right {@link RunContext} happens in each
|
|
111765
|
+
* handler via the inbound message's cohortId.
|
|
111637
111766
|
*/
|
|
111638
111767
|
#registerHandlers() {
|
|
111639
111768
|
if (this.#handlersRegistered) return;
|
|
@@ -111654,23 +111783,25 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111654
111783
|
}
|
|
111655
111784
|
/**
|
|
111656
111785
|
* Internal: message handlers for each protocol step. Each handler:
|
|
111657
|
-
* 1)
|
|
111658
|
-
* 2)
|
|
111659
|
-
* 3)
|
|
111786
|
+
* 1) resolves the cohort the message belongs to (by cohortId); ignores it if unknown
|
|
111787
|
+
* 2) feeds the message into the state machine via session.receive()
|
|
111788
|
+
* 3) emits a high-level event (carrying cohortId) for external observers
|
|
111789
|
+
* 4) checks if the new state triggers any automatic next steps, and if so:
|
|
111660
111790
|
* a) calls the appropriate decision callback(s)
|
|
111661
111791
|
* b) sends any resulting messages from the state machine
|
|
111792
|
+
* Errors fail only the owning cohort. A stopped runner ignores messages.
|
|
111662
111793
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111663
111794
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111664
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111665
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111666
111795
|
*/
|
|
111667
111796
|
async #handleOptIn(msg) {
|
|
111668
111797
|
if (this.#stopped) return;
|
|
111798
|
+
const ctx = this.#contextFor(msg);
|
|
111799
|
+
if (!ctx) return;
|
|
111669
111800
|
try {
|
|
111670
111801
|
this.session.receive(msg);
|
|
111671
|
-
this.#drainRejections();
|
|
111672
|
-
this.#onPhaseMaybeChanged();
|
|
111673
|
-
const optIn = this.session.pendingOptIns(
|
|
111802
|
+
this.#drainRejections(ctx);
|
|
111803
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111804
|
+
const optIn = this.session.pendingOptIns(ctx.cohortId).get(msg.from);
|
|
111674
111805
|
if (!optIn) return;
|
|
111675
111806
|
this.emit("opt-in-received", optIn);
|
|
111676
111807
|
if (optIn.communicationPk) {
|
|
@@ -111678,34 +111809,34 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111678
111809
|
}
|
|
111679
111810
|
const decision = await this.#onOptInReceived(optIn);
|
|
111680
111811
|
if (!decision.accepted) return;
|
|
111681
|
-
const maxParticipants =
|
|
111682
|
-
const cohortNow = this.session.getCohort(
|
|
111812
|
+
const maxParticipants = ctx.config.maxParticipants;
|
|
111813
|
+
const cohortNow = this.session.getCohort(ctx.cohortId);
|
|
111683
111814
|
if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
|
|
111684
111815
|
return;
|
|
111685
111816
|
}
|
|
111686
|
-
await this.#sendAll(this.session.acceptParticipant(
|
|
111687
|
-
this.emit("participant-accepted", { participantDid: msg.from });
|
|
111688
|
-
const cohort = this.session.getCohort(
|
|
111689
|
-
if (cohort.participants.length >=
|
|
111690
|
-
|
|
111817
|
+
await this.#sendAll(this.session.acceptParticipant(ctx.cohortId, msg.from));
|
|
111818
|
+
this.emit("participant-accepted", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111819
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111820
|
+
if (cohort.participants.length >= ctx.config.minParticipants && !ctx.finalizing) {
|
|
111821
|
+
ctx.finalizing = true;
|
|
111691
111822
|
const finalizeDecision = await this.#onReadyToFinalize({
|
|
111692
111823
|
acceptedCount: cohort.participants.length,
|
|
111693
|
-
minRequired:
|
|
111824
|
+
minRequired: ctx.config.minParticipants
|
|
111694
111825
|
});
|
|
111695
111826
|
if (!finalizeDecision.finalize) {
|
|
111696
|
-
|
|
111827
|
+
ctx.finalizing = false;
|
|
111697
111828
|
return;
|
|
111698
111829
|
}
|
|
111699
|
-
const readyMsgs = this.session.finalizeKeygen(
|
|
111700
|
-
this.#stopAdvertRepeating();
|
|
111830
|
+
const readyMsgs = this.session.finalizeKeygen(ctx.cohortId);
|
|
111831
|
+
this.#stopAdvertRepeating(ctx);
|
|
111701
111832
|
this.emit("keygen-complete", {
|
|
111702
|
-
cohortId:
|
|
111833
|
+
cohortId: ctx.cohortId,
|
|
111703
111834
|
beaconAddress: cohort.beaconAddress
|
|
111704
111835
|
});
|
|
111705
111836
|
await this.#sendAll(readyMsgs);
|
|
111706
111837
|
}
|
|
111707
111838
|
} catch (err) {
|
|
111708
|
-
this.#
|
|
111839
|
+
this.#failCohort(ctx, err);
|
|
111709
111840
|
}
|
|
111710
111841
|
}
|
|
111711
111842
|
/**
|
|
@@ -111713,23 +111844,23 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111713
111844
|
* and distributes the data for validation.
|
|
111714
111845
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111715
111846
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111716
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111717
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111718
111847
|
*/
|
|
111719
111848
|
async #handleSubmitUpdate(msg) {
|
|
111720
111849
|
if (this.#stopped) return;
|
|
111850
|
+
const ctx = this.#contextFor(msg);
|
|
111851
|
+
if (!ctx) return;
|
|
111721
111852
|
try {
|
|
111722
111853
|
this.session.receive(msg);
|
|
111723
|
-
this.#drainRejections();
|
|
111724
|
-
this.#onPhaseMaybeChanged();
|
|
111725
|
-
this.emit("update-received", { participantDid: msg.from });
|
|
111726
|
-
if (this.session.getCohortPhase(
|
|
111727
|
-
const distributeMsgs = this.session.buildAndDistribute(
|
|
111728
|
-
this.emit("data-distributed", { cohortId:
|
|
111854
|
+
this.#drainRejections(ctx);
|
|
111855
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111856
|
+
this.emit("update-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111857
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "UpdatesCollected" /* UpdatesCollected */) {
|
|
111858
|
+
const distributeMsgs = this.session.buildAndDistribute(ctx.cohortId);
|
|
111859
|
+
this.emit("data-distributed", { cohortId: ctx.cohortId });
|
|
111729
111860
|
await this.#sendAll(distributeMsgs);
|
|
111730
111861
|
}
|
|
111731
111862
|
} catch (err) {
|
|
111732
|
-
this.#
|
|
111863
|
+
this.#failCohort(ctx, err);
|
|
111733
111864
|
}
|
|
111734
111865
|
}
|
|
111735
111866
|
/**
|
|
@@ -111737,111 +111868,95 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111737
111868
|
* automatically requests tx data and starts signing.
|
|
111738
111869
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111739
111870
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111740
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111741
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111742
111871
|
*/
|
|
111743
111872
|
async #handleValidationAck(msg) {
|
|
111744
111873
|
if (this.#stopped) return;
|
|
111874
|
+
const ctx = this.#contextFor(msg);
|
|
111875
|
+
if (!ctx) return;
|
|
111745
111876
|
try {
|
|
111746
111877
|
this.session.receive(msg);
|
|
111747
|
-
this.#drainRejections();
|
|
111748
|
-
this.#onPhaseMaybeChanged();
|
|
111878
|
+
this.#drainRejections(ctx);
|
|
111879
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111749
111880
|
const approved = !!msg.body?.approved;
|
|
111750
|
-
this.emit("validation-received", { participantDid: msg.from, approved });
|
|
111751
|
-
const phase = this.session.getCohortPhase(
|
|
111881
|
+
this.emit("validation-received", { cohortId: ctx.cohortId, participantDid: msg.from, approved });
|
|
111882
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111752
111883
|
if (phase === "Failed" /* Failed */) {
|
|
111753
111884
|
const reason = `Validation rejected by participant ${msg.from}`;
|
|
111754
|
-
this.emit("cohort-failed", { cohortId:
|
|
111755
|
-
this.#
|
|
111885
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111886
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111756
111887
|
return;
|
|
111757
111888
|
}
|
|
111758
111889
|
if (phase === "Validated" /* Validated */) {
|
|
111759
|
-
const cohort = this.session.getCohort(
|
|
111890
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111760
111891
|
const txData = await this.#onProvideTxData({
|
|
111761
|
-
cohortId:
|
|
111892
|
+
cohortId: ctx.cohortId,
|
|
111762
111893
|
beaconAddress: cohort.beaconAddress,
|
|
111763
111894
|
signalBytes: cohort.signalBytes
|
|
111764
111895
|
});
|
|
111765
|
-
const authMsgs = this.session.startSigning(
|
|
111766
|
-
const sessionId = this.session.getSigningSessionId(
|
|
111767
|
-
this.emit("signing-started", { sessionId });
|
|
111896
|
+
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
111897
|
+
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
111898
|
+
this.emit("signing-started", { cohortId: ctx.cohortId, sessionId });
|
|
111768
111899
|
await this.#sendAll(authMsgs);
|
|
111769
111900
|
}
|
|
111770
111901
|
} catch (err) {
|
|
111771
|
-
this.#
|
|
111902
|
+
this.#failCohort(ctx, err);
|
|
111772
111903
|
}
|
|
111773
111904
|
}
|
|
111774
111905
|
/**
|
|
111775
|
-
* Handler for receiving nonce contributions
|
|
111776
|
-
*
|
|
111906
|
+
* Handler for receiving nonce contributions. When all nonces are received, sends the aggregated
|
|
111907
|
+
* nonce back to the cohort.
|
|
111777
111908
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111778
111909
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111779
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111780
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111781
111910
|
*/
|
|
111782
111911
|
async #handleNonceContribution(msg) {
|
|
111783
111912
|
if (this.#stopped) return;
|
|
111913
|
+
const ctx = this.#contextFor(msg);
|
|
111914
|
+
if (!ctx) return;
|
|
111784
111915
|
try {
|
|
111785
111916
|
this.session.receive(msg);
|
|
111786
|
-
this.#drainRejections();
|
|
111787
|
-
this.#onPhaseMaybeChanged();
|
|
111788
|
-
this.emit("nonce-received", { participantDid: msg.from });
|
|
111789
|
-
if (this.session.getCohortPhase(
|
|
111790
|
-
await this.#sendAll(this.session.sendAggregatedNonce(
|
|
111917
|
+
this.#drainRejections(ctx);
|
|
111918
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111919
|
+
this.emit("nonce-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111920
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "NoncesCollected" /* NoncesCollected */) {
|
|
111921
|
+
await this.#sendAll(this.session.sendAggregatedNonce(ctx.cohortId));
|
|
111791
111922
|
}
|
|
111792
111923
|
} catch (err) {
|
|
111793
|
-
this.#
|
|
111924
|
+
this.#failCohort(ctx, err);
|
|
111794
111925
|
}
|
|
111795
111926
|
}
|
|
111796
111927
|
/**
|
|
111797
111928
|
* Handler for receiving signature authorizations. When all partial signatures are received, the
|
|
111798
|
-
* session automatically completes
|
|
111929
|
+
* session automatically completes; the final result is emitted and the cohort's completion
|
|
111930
|
+
* promise resolves.
|
|
111799
111931
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111800
111932
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111801
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111802
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111803
111933
|
*/
|
|
111804
111934
|
async #handleSignatureAuthorization(msg) {
|
|
111805
111935
|
if (this.#stopped) return;
|
|
111936
|
+
const ctx = this.#contextFor(msg);
|
|
111937
|
+
if (!ctx) return;
|
|
111806
111938
|
try {
|
|
111807
111939
|
this.session.receive(msg);
|
|
111808
|
-
this.#drainRejections();
|
|
111809
|
-
this.#onPhaseMaybeChanged();
|
|
111810
|
-
const result = this.session.getResult(
|
|
111940
|
+
this.#drainRejections(ctx);
|
|
111941
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111942
|
+
const result = this.session.getResult(ctx.cohortId);
|
|
111811
111943
|
if (result) {
|
|
111812
|
-
this.#
|
|
111813
|
-
this.#unregisterHandlers();
|
|
111814
|
-
this.emit("signing-complete", result);
|
|
111815
|
-
this.#resolveRun?.(result);
|
|
111944
|
+
this.#completeCohort(ctx, result);
|
|
111816
111945
|
}
|
|
111817
111946
|
} catch (err) {
|
|
111818
|
-
this.#
|
|
111947
|
+
this.#failCohort(ctx, err);
|
|
111819
111948
|
}
|
|
111820
111949
|
}
|
|
111821
111950
|
/**
|
|
111822
111951
|
* Internal: helper to send all messages sequentially. Catches and propagates errors.
|
|
111823
111952
|
* @param {BaseMessage[]} msgs - The messages to send.
|
|
111824
111953
|
* @returns {Promise<void>} Resolves when all messages have been sent.
|
|
111825
|
-
* @throws {Error} If sending any message fails, the error is emitted and the run promise is
|
|
111826
|
-
* rejected.
|
|
111827
111954
|
*/
|
|
111828
111955
|
async #sendAll(msgs) {
|
|
111829
111956
|
for (const m2 of msgs) {
|
|
111830
111957
|
await this.#transport.sendMessage(m2, this.#did, m2.to);
|
|
111831
111958
|
}
|
|
111832
111959
|
}
|
|
111833
|
-
/**
|
|
111834
|
-
* Internal: helper to handle errors. Emits an 'error' event and rejects the run promise.
|
|
111835
|
-
* @param {Error} err - The error to handle.
|
|
111836
|
-
*/
|
|
111837
|
-
#fail(err) {
|
|
111838
|
-
this.#stopAdvertRepeating();
|
|
111839
|
-
this.#clearTimers();
|
|
111840
|
-
this.#unregisterHandlers();
|
|
111841
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111842
|
-
this.emit("error", err);
|
|
111843
|
-
this.#rejectRun?.(err);
|
|
111844
|
-
}
|
|
111845
111960
|
};
|
|
111846
111961
|
|
|
111847
111962
|
// src/core/aggregation/runner/participant-runner.ts
|
|
@@ -111902,7 +112017,8 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
111902
112017
|
}
|
|
111903
112018
|
/**
|
|
111904
112019
|
* Single-shot helper: start, join the first cohort that passes `shouldJoin`,
|
|
111905
|
-
* drive it to completion, and resolve. Convenient for tests and demos.
|
|
112020
|
+
* drive it to completion, and resolve. Convenient for tests and demos. The
|
|
112021
|
+
* single-cohort special case of {@link joinMatching} (count = 1).
|
|
111906
112022
|
*/
|
|
111907
112023
|
static async joinFirst(options2) {
|
|
111908
112024
|
return new Promise((resolve, reject) => {
|
|
@@ -111915,6 +112031,37 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
111915
112031
|
runner.start().catch(reject);
|
|
111916
112032
|
});
|
|
111917
112033
|
}
|
|
112034
|
+
/**
|
|
112035
|
+
* Multi-cohort helper: start, join EVERY cohort whose advert passes
|
|
112036
|
+
* `shouldJoin`, drive each to completion in parallel, and resolve once
|
|
112037
|
+
* `count` cohorts have completed (the runner stops at that point). The
|
|
112038
|
+
* N-cohort generalization of {@link joinFirst}, for a participant that joins
|
|
112039
|
+
* several cohorts advertised by one service.
|
|
112040
|
+
*
|
|
112041
|
+
* For an open-ended, long-lived subscriber (no fixed count), construct an
|
|
112042
|
+
* {@link AggregationParticipantRunner} directly, set `shouldJoin`, call
|
|
112043
|
+
* `start()`, and listen for `cohort-complete` — the runner already drives
|
|
112044
|
+
* any number of cohorts concurrently.
|
|
112045
|
+
*
|
|
112046
|
+
* @param options Participant runner options (set `shouldJoin` to select cohorts).
|
|
112047
|
+
* @param count Number of completed cohorts to collect before resolving.
|
|
112048
|
+
* @returns The {@link CohortCompleteInfo} for each completed cohort, in completion order.
|
|
112049
|
+
*/
|
|
112050
|
+
static async joinMatching(options2, count) {
|
|
112051
|
+
return new Promise((resolve, reject) => {
|
|
112052
|
+
const runner = new _AggregationParticipantRunner(options2);
|
|
112053
|
+
const completed = [];
|
|
112054
|
+
runner.on("cohort-complete", (info) => {
|
|
112055
|
+
completed.push(info);
|
|
112056
|
+
if (completed.length >= count) {
|
|
112057
|
+
runner.stop();
|
|
112058
|
+
resolve(completed);
|
|
112059
|
+
}
|
|
112060
|
+
});
|
|
112061
|
+
runner.on("error", reject);
|
|
112062
|
+
runner.start().catch(reject);
|
|
112063
|
+
});
|
|
112064
|
+
}
|
|
111918
112065
|
/**
|
|
111919
112066
|
* Internal: handler registration with the transport. Idempotent and safe to call multiple times,
|
|
111920
112067
|
* but only registers handlers once.
|
|
@@ -112063,7 +112210,7 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
112063
112210
|
if (this.session.getCohortPhase(cohortId) === "Complete" /* Complete */) {
|
|
112064
112211
|
const info = this.session.joinedCohorts.get(cohortId);
|
|
112065
112212
|
if (info) {
|
|
112066
|
-
const validation = this.session.
|
|
112213
|
+
const validation = this.session.getValidation(cohortId);
|
|
112067
112214
|
this.emit("cohort-complete", {
|
|
112068
112215
|
cohortId,
|
|
112069
112216
|
beaconAddress: info.beaconAddress,
|