@did-btcr2/method 0.26.0 → 0.28.0
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 +118 -236
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +27665 -23764
- package/dist/browser.mjs +27665 -23764
- package/dist/cjs/index.js +2661 -2239
- package/dist/esm/core/aggregation/cohort.js +178 -0
- package/dist/esm/core/aggregation/cohort.js.map +1 -0
- package/dist/esm/core/aggregation/errors.js +22 -0
- package/dist/esm/core/aggregation/errors.js.map +1 -0
- package/dist/esm/core/{beacon/aggregation/cohort → aggregation}/messages/base.js +0 -1
- package/dist/esm/core/aggregation/messages/base.js.map +1 -0
- package/dist/esm/core/aggregation/messages/constants.js +26 -0
- package/dist/esm/core/aggregation/messages/constants.js.map +1 -0
- package/dist/esm/core/aggregation/messages/factories.js +113 -0
- package/dist/esm/core/aggregation/messages/factories.js.map +1 -0
- package/dist/esm/core/aggregation/messages/guards.js +37 -0
- package/dist/esm/core/aggregation/messages/guards.js.map +1 -0
- package/dist/esm/core/aggregation/messages/index.js +5 -0
- package/dist/esm/core/aggregation/messages/index.js.map +1 -0
- package/dist/esm/core/aggregation/participant.js +376 -0
- package/dist/esm/core/aggregation/participant.js.map +1 -0
- package/dist/esm/core/aggregation/phases.js +39 -0
- package/dist/esm/core/aggregation/phases.js.map +1 -0
- package/dist/esm/core/aggregation/runner/events.js +2 -0
- package/dist/esm/core/aggregation/runner/events.js.map +1 -0
- package/dist/esm/core/aggregation/runner/index.js +5 -0
- package/dist/esm/core/aggregation/runner/index.js.map +1 -0
- package/dist/esm/core/aggregation/runner/participant-runner.js +286 -0
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -0
- package/dist/esm/core/aggregation/runner/service-runner.js +290 -0
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -0
- package/dist/esm/core/aggregation/runner/typed-emitter.js +80 -0
- package/dist/esm/core/aggregation/runner/typed-emitter.js.map +1 -0
- package/dist/esm/core/aggregation/service.js +416 -0
- package/dist/esm/core/aggregation/service.js.map +1 -0
- package/dist/esm/core/aggregation/signing-session.js +133 -0
- package/dist/esm/core/aggregation/signing-session.js.map +1 -0
- package/dist/esm/core/aggregation/transport/didcomm.js +32 -0
- package/dist/esm/core/aggregation/transport/didcomm.js.map +1 -0
- package/dist/esm/core/aggregation/transport/error.js +12 -0
- package/dist/esm/core/aggregation/transport/error.js.map +1 -0
- package/dist/esm/core/aggregation/transport/factory.js +20 -0
- package/dist/esm/core/aggregation/transport/factory.js.map +1 -0
- package/dist/esm/core/aggregation/transport/index.js +6 -0
- package/dist/esm/core/aggregation/transport/index.js.map +1 -0
- package/dist/esm/core/aggregation/transport/nostr.js +262 -0
- package/dist/esm/core/aggregation/transport/nostr.js.map +1 -0
- package/dist/esm/core/aggregation/transport/transport.js +2 -0
- package/dist/esm/core/aggregation/transport/transport.js.map +1 -0
- package/dist/esm/core/beacon/beacon.js +80 -0
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/beacon/cas-beacon.js +15 -56
- package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
- package/dist/esm/core/beacon/error.js +0 -10
- package/dist/esm/core/beacon/error.js.map +1 -1
- package/dist/esm/core/beacon/fee-estimator.js +30 -0
- package/dist/esm/core/beacon/fee-estimator.js.map +1 -0
- package/dist/esm/core/beacon/singleton-beacon.js +10 -53
- package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
- package/dist/esm/core/beacon/smt-beacon.js +85 -9
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/identifier.js +13 -0
- package/dist/esm/core/identifier.js.map +1 -1
- package/dist/esm/core/resolver.js +9 -0
- package/dist/esm/core/resolver.js.map +1 -1
- package/dist/esm/core/updater.js +269 -0
- package/dist/esm/core/updater.js.map +1 -0
- package/dist/esm/did-btcr2.js +30 -42
- package/dist/esm/did-btcr2.js.map +1 -1
- package/dist/esm/index.js +16 -25
- package/dist/esm/index.js.map +1 -1
- package/dist/types/core/aggregation/cohort.d.ts +94 -0
- package/dist/types/core/aggregation/cohort.d.ts.map +1 -0
- package/dist/types/core/aggregation/errors.d.ts +14 -0
- package/dist/types/core/aggregation/errors.d.ts.map +1 -0
- package/dist/types/core/{beacon/aggregation/cohort → aggregation}/messages/base.d.ts +7 -1
- package/dist/types/core/aggregation/messages/base.d.ts.map +1 -0
- package/dist/types/core/aggregation/messages/constants.d.ts +23 -0
- package/dist/types/core/aggregation/messages/constants.d.ts.map +1 -0
- package/dist/types/core/aggregation/messages/factories.d.ts +177 -0
- package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -0
- package/dist/types/core/aggregation/messages/guards.d.ts +11 -0
- package/dist/types/core/aggregation/messages/guards.d.ts.map +1 -0
- package/dist/types/core/aggregation/messages/index.d.ts +5 -0
- package/dist/types/core/aggregation/messages/index.d.ts.map +1 -0
- package/dist/types/core/aggregation/participant.d.ts +101 -0
- package/dist/types/core/aggregation/participant.d.ts.map +1 -0
- package/dist/types/core/aggregation/phases.d.ts +49 -0
- package/dist/types/core/aggregation/phases.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/events.d.ts +89 -0
- package/dist/types/core/aggregation/runner/events.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/index.d.ts +5 -0
- package/dist/types/core/aggregation/runner/index.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/participant-runner.d.ts +111 -0
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/service-runner.d.ts +102 -0
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/typed-emitter.d.ts +41 -0
- package/dist/types/core/aggregation/runner/typed-emitter.d.ts.map +1 -0
- package/dist/types/core/aggregation/service.d.ts +112 -0
- package/dist/types/core/aggregation/service.d.ts.map +1 -0
- package/dist/types/core/aggregation/signing-session.d.ts +69 -0
- package/dist/types/core/aggregation/signing-session.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/didcomm.d.ts +20 -0
- package/dist/types/core/aggregation/transport/didcomm.d.ts.map +1 -0
- package/dist/types/core/{beacon/aggregation/communication → aggregation/transport}/error.d.ts +2 -2
- package/dist/types/core/aggregation/transport/error.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/factory.d.ts +13 -0
- package/dist/types/core/aggregation/transport/factory.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/index.d.ts +6 -0
- package/dist/types/core/aggregation/transport/index.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/nostr.d.ts +55 -0
- package/dist/types/core/aggregation/transport/nostr.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/transport.d.ts +37 -0
- package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -0
- package/dist/types/core/beacon/beacon.d.ts +37 -2
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/cas-beacon.d.ts +19 -7
- package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/error.d.ts +0 -6
- package/dist/types/core/beacon/error.d.ts.map +1 -1
- package/dist/types/core/beacon/fee-estimator.d.ts +40 -0
- package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -0
- package/dist/types/core/beacon/interfaces.d.ts +8 -0
- package/dist/types/core/beacon/interfaces.d.ts.map +1 -1
- package/dist/types/core/beacon/singleton-beacon.d.ts +9 -2
- package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/smt-beacon.d.ts +27 -7
- package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
- package/dist/types/core/identifier.d.ts +8 -0
- package/dist/types/core/identifier.d.ts.map +1 -1
- package/dist/types/core/interfaces.d.ts +2 -2
- package/dist/types/core/resolver.d.ts +11 -1
- package/dist/types/core/resolver.d.ts.map +1 -1
- package/dist/types/core/updater.d.ts +178 -0
- package/dist/types/core/updater.d.ts.map +1 -0
- package/dist/types/did-btcr2.d.ts +23 -23
- package/dist/types/did-btcr2.d.ts.map +1 -1
- package/dist/types/index.d.ts +11 -25
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +31 -30
- package/src/core/aggregation/cohort.ts +247 -0
- package/src/core/aggregation/errors.ts +25 -0
- package/src/core/{beacon/aggregation/cohort → aggregation}/messages/base.ts +8 -3
- package/src/core/aggregation/messages/constants.ts +28 -0
- package/src/core/aggregation/messages/factories.ts +240 -0
- package/src/core/aggregation/messages/guards.ts +55 -0
- package/src/core/aggregation/messages/index.ts +4 -0
- package/src/core/aggregation/participant.ts +510 -0
- package/src/core/aggregation/phases.ts +82 -0
- package/src/core/aggregation/runner/events.ts +77 -0
- package/src/core/aggregation/runner/index.ts +4 -0
- package/src/core/aggregation/runner/participant-runner.ts +364 -0
- package/src/core/aggregation/runner/service-runner.ts +365 -0
- package/src/core/aggregation/runner/typed-emitter.ts +87 -0
- package/src/core/aggregation/service.ts +547 -0
- package/src/core/aggregation/signing-session.ts +209 -0
- package/src/core/aggregation/transport/didcomm.ts +42 -0
- package/src/core/aggregation/transport/error.ts +13 -0
- package/src/core/aggregation/transport/factory.ts +29 -0
- package/src/core/aggregation/transport/index.ts +5 -0
- package/src/core/aggregation/transport/nostr.ts +333 -0
- package/src/core/aggregation/transport/transport.ts +46 -0
- package/src/core/beacon/beacon.ts +122 -2
- package/src/core/beacon/cas-beacon.ts +28 -76
- package/src/core/beacon/error.ts +0 -12
- package/src/core/beacon/fee-estimator.ts +52 -0
- package/src/core/beacon/interfaces.ts +10 -1
- package/src/core/beacon/singleton-beacon.ts +14 -75
- package/src/core/beacon/smt-beacon.ts +109 -11
- package/src/core/identifier.ts +17 -0
- package/src/core/interfaces.ts +2 -2
- package/src/core/resolver.ts +25 -2
- package/src/core/updater.ts +415 -0
- package/src/did-btcr2.ts +36 -66
- package/src/index.ts +17 -30
- package/dist/esm/core/beacon/aggregation/cohort/index.js +0 -237
- package/dist/esm/core/beacon/aggregation/cohort/index.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/base.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/constants.js +0 -11
- package/dist/esm/core/beacon/aggregation/cohort/messages/constants.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/index.js +0 -98
- package/dist/esm/core/beacon/aggregation/cohort/messages/index.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.js +0 -31
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.js +0 -29
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.js +0 -27
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/opt-in.js +0 -23
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/opt-in.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/subscribe.js +0 -28
- package/dist/esm/core/beacon/aggregation/cohort/messages/keygen/subscribe.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.js +0 -29
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/authorization-request.js +0 -30
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/authorization-request.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.js +0 -30
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/request-signature.js +0 -30
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/request-signature.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/signature-authorization.js +0 -31
- package/dist/esm/core/beacon/aggregation/cohort/messages/sign/signature-authorization.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/cohort/status.js +0 -8
- package/dist/esm/core/beacon/aggregation/cohort/status.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js +0 -121
- package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js +0 -245
- package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/communication/error.js +0 -12
- package/dist/esm/core/beacon/aggregation/communication/error.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/communication/factory.js +0 -21
- package/dist/esm/core/beacon/aggregation/communication/factory.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/communication/service.js +0 -2
- package/dist/esm/core/beacon/aggregation/communication/service.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/coordinator.js +0 -343
- package/dist/esm/core/beacon/aggregation/coordinator.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/participant.js +0 -435
- package/dist/esm/core/beacon/aggregation/participant.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/session/index.js +0 -244
- package/dist/esm/core/beacon/aggregation/session/index.js.map +0 -1
- package/dist/esm/core/beacon/aggregation/session/status.js +0 -11
- package/dist/esm/core/beacon/aggregation/session/status.js.map +0 -1
- package/dist/esm/core/update.js +0 -112
- package/dist/esm/core/update.js.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/index.d.ts +0 -136
- package/dist/types/core/beacon/aggregation/cohort/index.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/base.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/constants.d.ts +0 -11
- package/dist/types/core/beacon/aggregation/cohort/messages/constants.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/index.d.ts +0 -65
- package/dist/types/core/beacon/aggregation/cohort/messages/index.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.d.ts +0 -29
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.d.ts +0 -26
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.d.ts +0 -24
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in.d.ts +0 -20
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/subscribe.d.ts +0 -25
- package/dist/types/core/beacon/aggregation/cohort/messages/keygen/subscribe.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.d.ts +0 -25
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/authorization-request.d.ts +0 -26
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/authorization-request.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.d.ts +0 -26
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/request-signature.d.ts +0 -26
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/request-signature.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/signature-authorization.d.ts +0 -27
- package/dist/types/core/beacon/aggregation/cohort/messages/sign/signature-authorization.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/cohort/status.d.ts +0 -8
- package/dist/types/core/beacon/aggregation/cohort/status.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/communication/adapter/did-comm.d.ts +0 -89
- package/dist/types/core/beacon/aggregation/communication/adapter/did-comm.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/communication/adapter/nostr.d.ts +0 -103
- package/dist/types/core/beacon/aggregation/communication/adapter/nostr.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/communication/error.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/communication/factory.d.ts +0 -10
- package/dist/types/core/beacon/aggregation/communication/factory.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/communication/service.d.ts +0 -36
- package/dist/types/core/beacon/aggregation/communication/service.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/coordinator.d.ts +0 -116
- package/dist/types/core/beacon/aggregation/coordinator.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/participant.d.ts +0 -192
- package/dist/types/core/beacon/aggregation/participant.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/session/index.d.ts +0 -156
- package/dist/types/core/beacon/aggregation/session/index.d.ts.map +0 -1
- package/dist/types/core/beacon/aggregation/session/status.d.ts +0 -11
- package/dist/types/core/beacon/aggregation/session/status.d.ts.map +0 -1
- package/dist/types/core/update.d.ts +0 -52
- package/dist/types/core/update.d.ts.map +0 -1
- package/src/core/beacon/aggregation/cohort/index.ts +0 -305
- package/src/core/beacon/aggregation/cohort/messages/constants.ts +0 -12
- package/src/core/beacon/aggregation/cohort/messages/index.ts +0 -143
- package/src/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.ts +0 -44
- package/src/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.ts +0 -40
- package/src/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.ts +0 -35
- package/src/core/beacon/aggregation/cohort/messages/keygen/opt-in.ts +0 -34
- package/src/core/beacon/aggregation/cohort/messages/keygen/subscribe.ts +0 -36
- package/src/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.ts +0 -39
- package/src/core/beacon/aggregation/cohort/messages/sign/authorization-request.ts +0 -40
- package/src/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.ts +0 -40
- package/src/core/beacon/aggregation/cohort/messages/sign/request-signature.ts +0 -40
- package/src/core/beacon/aggregation/cohort/messages/sign/signature-authorization.ts +0 -41
- package/src/core/beacon/aggregation/cohort/status.ts +0 -7
- package/src/core/beacon/aggregation/communication/adapter/did-comm.ts +0 -148
- package/src/core/beacon/aggregation/communication/adapter/nostr.ts +0 -323
- package/src/core/beacon/aggregation/communication/error.ts +0 -13
- package/src/core/beacon/aggregation/communication/factory.ts +0 -25
- package/src/core/beacon/aggregation/communication/service.ts +0 -42
- package/src/core/beacon/aggregation/coordinator.ts +0 -419
- package/src/core/beacon/aggregation/participant.ts +0 -517
- package/src/core/beacon/aggregation/session/index.ts +0 -301
- package/src/core/beacon/aggregation/session/status.ts +0 -18
- package/src/core/update.ts +0 -158
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
2
|
+
import type { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
3
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
4
|
+
import type { Transaction } from 'bitcoinjs-lib';
|
|
5
|
+
import { AggregationCohort } from './cohort.js';
|
|
6
|
+
import { AggregationServiceError } from './errors.js';
|
|
7
|
+
import type { BaseMessage } from './messages/base.js';
|
|
8
|
+
import {
|
|
9
|
+
COHORT_OPT_IN,
|
|
10
|
+
NONCE_CONTRIBUTION,
|
|
11
|
+
SIGNATURE_AUTHORIZATION,
|
|
12
|
+
SUBMIT_UPDATE,
|
|
13
|
+
VALIDATION_ACK,
|
|
14
|
+
} from './messages/constants.js';
|
|
15
|
+
import {
|
|
16
|
+
createAuthorizationRequestMessage,
|
|
17
|
+
createCohortAdvertMessage,
|
|
18
|
+
createCohortOptInAcceptMessage,
|
|
19
|
+
createCohortReadyMessage,
|
|
20
|
+
createDistributeAggregatedDataMessage,
|
|
21
|
+
createAggregatedNonceMessage,
|
|
22
|
+
} from './messages/factories.js';
|
|
23
|
+
import type { ServiceCohortPhaseType } from './phases.js';
|
|
24
|
+
import { ServiceCohortPhase } from './phases.js';
|
|
25
|
+
import { BeaconSigningSession } from './signing-session.js';
|
|
26
|
+
|
|
27
|
+
/** Cohort configuration set by the service operator. */
|
|
28
|
+
export interface CohortConfig {
|
|
29
|
+
minParticipants: number;
|
|
30
|
+
network: string;
|
|
31
|
+
beaconType: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Pending opt-in awaiting service operator approval. */
|
|
35
|
+
export interface PendingOptIn {
|
|
36
|
+
cohortId: string;
|
|
37
|
+
participantDid: string;
|
|
38
|
+
participantPk: Uint8Array;
|
|
39
|
+
communicationPk: Uint8Array;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Validation tracking progress. */
|
|
43
|
+
export interface ValidationProgress {
|
|
44
|
+
approved: ReadonlySet<string>;
|
|
45
|
+
rejected: ReadonlySet<string>;
|
|
46
|
+
pending: ReadonlySet<string>;
|
|
47
|
+
total: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Final aggregation result for a cohort. */
|
|
51
|
+
export interface AggregationResult {
|
|
52
|
+
cohortId: string;
|
|
53
|
+
signature: Uint8Array;
|
|
54
|
+
signedTx: Transaction;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Transaction data needed to start a signing session. */
|
|
58
|
+
export interface SigningTxData {
|
|
59
|
+
tx: Transaction;
|
|
60
|
+
prevOutScripts: Uint8Array[];
|
|
61
|
+
prevOutValues: bigint[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Per-cohort service state — internal. */
|
|
65
|
+
interface ServiceCohortState {
|
|
66
|
+
phase: ServiceCohortPhaseType;
|
|
67
|
+
cohort: AggregationCohort;
|
|
68
|
+
config: CohortConfig;
|
|
69
|
+
pendingOptIns: Map<string, PendingOptIn>;
|
|
70
|
+
acceptedParticipants: Set<string>;
|
|
71
|
+
signingSession?: BeaconSigningSession;
|
|
72
|
+
result?: AggregationResult;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface AggregationServiceParams {
|
|
76
|
+
did: string;
|
|
77
|
+
keys: SchnorrKeyPair;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Sans-I/O state machine for an Aggregation Service.
|
|
82
|
+
*
|
|
83
|
+
* Manages multiple cohorts simultaneously. The service operator drives the
|
|
84
|
+
* state machine via `receive()` (for incoming messages) and explicit action
|
|
85
|
+
* methods (advertising, accepting opt-ins, finalizing keygen, building
|
|
86
|
+
* aggregated data, starting signing). All outgoing messages are returned for
|
|
87
|
+
* the caller to send via whatever transport.
|
|
88
|
+
*
|
|
89
|
+
* @class AggregationService
|
|
90
|
+
*/
|
|
91
|
+
export class AggregationService {
|
|
92
|
+
readonly did: string;
|
|
93
|
+
readonly keys: SchnorrKeyPair;
|
|
94
|
+
|
|
95
|
+
/** Per-cohort state, keyed by cohortId. */
|
|
96
|
+
#cohortStates: Map<string, ServiceCohortState> = new Map();
|
|
97
|
+
|
|
98
|
+
constructor({ did, keys }: AggregationServiceParams) {
|
|
99
|
+
this.did = did;
|
|
100
|
+
this.keys = keys;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
receive(message: BaseMessage): void {
|
|
105
|
+
const type = message.type;
|
|
106
|
+
switch(type) {
|
|
107
|
+
case COHORT_OPT_IN:
|
|
108
|
+
this.#handleOptIn(message);
|
|
109
|
+
break;
|
|
110
|
+
case SUBMIT_UPDATE:
|
|
111
|
+
this.#handleSubmitUpdate(message);
|
|
112
|
+
break;
|
|
113
|
+
case VALIDATION_ACK:
|
|
114
|
+
this.#handleValidationAck(message);
|
|
115
|
+
break;
|
|
116
|
+
case NONCE_CONTRIBUTION:
|
|
117
|
+
this.#handleNonceContribution(message);
|
|
118
|
+
break;
|
|
119
|
+
case SIGNATURE_AUTHORIZATION:
|
|
120
|
+
this.#handleSignatureAuthorization(message);
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
// Unknown message type — silently ignore
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create a new cohort with the given config. Returns the cohort ID.
|
|
131
|
+
* Cohort starts in `Created` phase — call `advertise()` to broadcast.
|
|
132
|
+
*/
|
|
133
|
+
createCohort(config: CohortConfig): string {
|
|
134
|
+
const cohort = new AggregationCohort({
|
|
135
|
+
serviceDid : this.did,
|
|
136
|
+
minParticipants : config.minParticipants,
|
|
137
|
+
network : config.network,
|
|
138
|
+
beaconType : config.beaconType,
|
|
139
|
+
});
|
|
140
|
+
this.#cohortStates.set(cohort.id, {
|
|
141
|
+
phase : ServiceCohortPhase.Created,
|
|
142
|
+
cohort,
|
|
143
|
+
config,
|
|
144
|
+
pendingOptIns : new Map(),
|
|
145
|
+
acceptedParticipants : new Set(),
|
|
146
|
+
});
|
|
147
|
+
return cohort.id;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Advertise a cohort to discover participants.
|
|
152
|
+
* Returns the advert message to broadcast.
|
|
153
|
+
*/
|
|
154
|
+
advertise(cohortId: string): BaseMessage[] {
|
|
155
|
+
const state = this.#cohortStates.get(cohortId);
|
|
156
|
+
if(!state) {
|
|
157
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
158
|
+
}
|
|
159
|
+
if(state.phase !== ServiceCohortPhase.Created) {
|
|
160
|
+
throw new AggregationServiceError(
|
|
161
|
+
`Cannot advertise cohort ${cohortId}: phase is ${state.phase}.`,
|
|
162
|
+
'INVALID_PHASE', { cohortId, phase: state.phase }
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const message = createCohortAdvertMessage({
|
|
167
|
+
from : this.did,
|
|
168
|
+
cohortId,
|
|
169
|
+
cohortSize : state.config.minParticipants,
|
|
170
|
+
beaconType : state.config.beaconType,
|
|
171
|
+
network : state.config.network,
|
|
172
|
+
communicationPk : this.keys.publicKey.compressed,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
state.phase = ServiceCohortPhase.Advertised;
|
|
176
|
+
return [message];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Pending opt-ins awaiting operator approval. */
|
|
180
|
+
pendingOptIns(cohortId: string): ReadonlyMap<string, PendingOptIn> {
|
|
181
|
+
const state = this.#cohortStates.get(cohortId);
|
|
182
|
+
if(!state) return new Map();
|
|
183
|
+
// Return only those not yet accepted
|
|
184
|
+
const map = new Map<string, PendingOptIn>();
|
|
185
|
+
for(const [did, optIn] of state.pendingOptIns) {
|
|
186
|
+
if(!state.acceptedParticipants.has(did)) {
|
|
187
|
+
map.set(did, optIn);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return map;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#handleOptIn(message: BaseMessage): void {
|
|
194
|
+
const cohortId = message.body?.cohortId;
|
|
195
|
+
if(!cohortId) return;
|
|
196
|
+
const state = this.#cohortStates.get(cohortId);
|
|
197
|
+
if(!state) return;
|
|
198
|
+
if(state.phase !== ServiceCohortPhase.Advertised) return;
|
|
199
|
+
|
|
200
|
+
const participantDid = message.from;
|
|
201
|
+
const participantPk = message.body?.participantPk;
|
|
202
|
+
const communicationPk = message.body?.communicationPk;
|
|
203
|
+
if(!participantPk || !communicationPk) return;
|
|
204
|
+
|
|
205
|
+
state.pendingOptIns.set(participantDid, {
|
|
206
|
+
cohortId,
|
|
207
|
+
participantDid,
|
|
208
|
+
participantPk,
|
|
209
|
+
communicationPk,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Service operator accepts a participant's opt-in.
|
|
215
|
+
* Returns the accept message to send.
|
|
216
|
+
*/
|
|
217
|
+
acceptParticipant(cohortId: string, participantDid: string): BaseMessage[] {
|
|
218
|
+
const state = this.#cohortStates.get(cohortId);
|
|
219
|
+
if(!state) {
|
|
220
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
221
|
+
}
|
|
222
|
+
const optIn = state.pendingOptIns.get(participantDid);
|
|
223
|
+
if(!optIn) {
|
|
224
|
+
throw new AggregationServiceError(
|
|
225
|
+
`No pending opt-in from ${participantDid} for cohort ${cohortId}.`,
|
|
226
|
+
'NO_OPT_IN', { cohortId, participantDid }
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
if(state.acceptedParticipants.has(participantDid)) {
|
|
230
|
+
throw new AggregationServiceError(
|
|
231
|
+
`Participant ${participantDid} already accepted into cohort ${cohortId}.`,
|
|
232
|
+
'ALREADY_ACCEPTED', { cohortId, participantDid }
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
state.acceptedParticipants.add(participantDid);
|
|
237
|
+
state.cohort.participants.push(participantDid);
|
|
238
|
+
state.cohort.cohortKeys = [...state.cohort.cohortKeys, optIn.participantPk];
|
|
239
|
+
|
|
240
|
+
return [createCohortOptInAcceptMessage({
|
|
241
|
+
from : this.did,
|
|
242
|
+
to : participantDid,
|
|
243
|
+
cohortId,
|
|
244
|
+
})];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Finalize cohort keygen: compute MuSig2 Taproot beacon address and send
|
|
249
|
+
* COHORT_READY messages to all accepted participants.
|
|
250
|
+
*/
|
|
251
|
+
finalizeKeygen(cohortId: string): BaseMessage[] {
|
|
252
|
+
const state = this.#cohortStates.get(cohortId);
|
|
253
|
+
if(!state) {
|
|
254
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
255
|
+
}
|
|
256
|
+
if(state.phase !== ServiceCohortPhase.Advertised) {
|
|
257
|
+
throw new AggregationServiceError(
|
|
258
|
+
`Cannot finalize keygen for cohort ${cohortId}: phase is ${state.phase}.`,
|
|
259
|
+
'INVALID_PHASE', { cohortId, phase: state.phase }
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if(state.acceptedParticipants.size < state.config.minParticipants) {
|
|
263
|
+
throw new AggregationServiceError(
|
|
264
|
+
`Cohort ${cohortId} has only ${state.acceptedParticipants.size} accepted participants, need ${state.config.minParticipants}.`,
|
|
265
|
+
'NOT_ENOUGH_PARTICIPANTS', { cohortId }
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const beaconAddress = state.cohort.computeBeaconAddress();
|
|
270
|
+
state.phase = ServiceCohortPhase.CohortSet;
|
|
271
|
+
|
|
272
|
+
const messages: BaseMessage[] = [];
|
|
273
|
+
for(const participantDid of state.cohort.participants) {
|
|
274
|
+
messages.push(createCohortReadyMessage({
|
|
275
|
+
from : this.did,
|
|
276
|
+
to : participantDid,
|
|
277
|
+
cohortId,
|
|
278
|
+
beaconAddress,
|
|
279
|
+
cohortKeys : state.cohort.cohortKeys,
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
return messages;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/** Updates collected so far for a cohort. */
|
|
287
|
+
collectedUpdates(cohortId: string): ReadonlyMap<string, SignedBTCR2Update> {
|
|
288
|
+
const state = this.#cohortStates.get(cohortId);
|
|
289
|
+
if(!state) return new Map();
|
|
290
|
+
return state.cohort.pendingUpdates;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#handleSubmitUpdate(message: BaseMessage): void {
|
|
294
|
+
const cohortId = message.body?.cohortId;
|
|
295
|
+
if(!cohortId) return;
|
|
296
|
+
const state = this.#cohortStates.get(cohortId);
|
|
297
|
+
if(!state) return;
|
|
298
|
+
if(state.phase !== ServiceCohortPhase.CohortSet && state.phase !== ServiceCohortPhase.CollectingUpdates) return;
|
|
299
|
+
|
|
300
|
+
const signedUpdate = message.body?.signedUpdate;
|
|
301
|
+
if(!signedUpdate) return;
|
|
302
|
+
|
|
303
|
+
state.cohort.addUpdate(message.from, signedUpdate as unknown as SignedBTCR2Update);
|
|
304
|
+
|
|
305
|
+
if(state.phase === ServiceCohortPhase.CohortSet) {
|
|
306
|
+
state.phase = ServiceCohortPhase.CollectingUpdates;
|
|
307
|
+
}
|
|
308
|
+
if(state.cohort.hasAllUpdates()) {
|
|
309
|
+
state.phase = ServiceCohortPhase.UpdatesCollected;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Build the aggregated data structure (CAS Announcement or SMT tree) and
|
|
316
|
+
* return distribute messages to send to all participants for validation.
|
|
317
|
+
*/
|
|
318
|
+
buildAndDistribute(cohortId: string): BaseMessage[] {
|
|
319
|
+
const state = this.#cohortStates.get(cohortId);
|
|
320
|
+
if(!state) {
|
|
321
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
322
|
+
}
|
|
323
|
+
if(state.phase !== ServiceCohortPhase.UpdatesCollected) {
|
|
324
|
+
throw new AggregationServiceError(
|
|
325
|
+
`Cannot build aggregated data for cohort ${cohortId}: phase is ${state.phase}.`,
|
|
326
|
+
'INVALID_PHASE', { cohortId, phase: state.phase }
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if(state.config.beaconType === 'CASBeacon') {
|
|
331
|
+
state.cohort.buildCASAnnouncement();
|
|
332
|
+
} else if(state.config.beaconType === 'SMTBeacon') {
|
|
333
|
+
state.cohort.buildSMTTree();
|
|
334
|
+
} else {
|
|
335
|
+
throw new AggregationServiceError(
|
|
336
|
+
`Unsupported beacon type: ${state.config.beaconType}`,
|
|
337
|
+
'UNSUPPORTED_BEACON_TYPE', { cohortId, beaconType: state.config.beaconType }
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const signalBytesHex = bytesToHex(state.cohort.signalBytes!);
|
|
342
|
+
state.phase = ServiceCohortPhase.DataDistributed;
|
|
343
|
+
|
|
344
|
+
const messages: BaseMessage[] = [];
|
|
345
|
+
for(const participantDid of state.cohort.participants) {
|
|
346
|
+
messages.push(createDistributeAggregatedDataMessage({
|
|
347
|
+
from : this.did,
|
|
348
|
+
to : participantDid,
|
|
349
|
+
cohortId,
|
|
350
|
+
beaconType : state.config.beaconType,
|
|
351
|
+
signalBytesHex,
|
|
352
|
+
casAnnouncement : state.config.beaconType === 'CASBeacon' ? state.cohort.casAnnouncement : undefined,
|
|
353
|
+
smtProof : state.config.beaconType === 'SMTBeacon' ? state.cohort.smtProofs?.get(participantDid) as unknown as Record<string, unknown> : undefined,
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
return messages;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
validationProgress(cohortId: string): ValidationProgress {
|
|
360
|
+
const state = this.#cohortStates.get(cohortId);
|
|
361
|
+
if(!state) {
|
|
362
|
+
return { approved: new Set(), rejected: new Set(), pending: new Set(), total: 0 };
|
|
363
|
+
}
|
|
364
|
+
const approved = state.cohort.validationAcks;
|
|
365
|
+
const rejected = state.cohort.validationRejections;
|
|
366
|
+
const allParticipants = new Set(state.cohort.participants);
|
|
367
|
+
const responded = new Set([...approved, ...rejected]);
|
|
368
|
+
const pending = new Set([...allParticipants].filter(p => !responded.has(p)));
|
|
369
|
+
return {
|
|
370
|
+
approved,
|
|
371
|
+
rejected,
|
|
372
|
+
pending,
|
|
373
|
+
total : allParticipants.size,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
#handleValidationAck(message: BaseMessage): void {
|
|
378
|
+
const cohortId = message.body?.cohortId;
|
|
379
|
+
if(!cohortId) return;
|
|
380
|
+
const state = this.#cohortStates.get(cohortId);
|
|
381
|
+
if(!state) return;
|
|
382
|
+
if(state.phase !== ServiceCohortPhase.DataDistributed) return;
|
|
383
|
+
|
|
384
|
+
const approved = message.body?.approved;
|
|
385
|
+
if(approved === undefined) return;
|
|
386
|
+
|
|
387
|
+
state.cohort.addValidation(message.from, approved);
|
|
388
|
+
|
|
389
|
+
// Transition to Validated only when all participants approved.
|
|
390
|
+
// Transition to Failed when all responses are in but at least one rejected.
|
|
391
|
+
if(state.cohort.isFullyValidated()) {
|
|
392
|
+
state.phase = ServiceCohortPhase.Validated;
|
|
393
|
+
} else if(state.cohort.hasAllValidationResponses()) {
|
|
394
|
+
state.phase = ServiceCohortPhase.Failed;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Start a signing session by creating auth requests for all participants.
|
|
401
|
+
* The caller provides the transaction data — typically built via
|
|
402
|
+
* `buildBeaconTransaction()` against a Bitcoin connection.
|
|
403
|
+
*/
|
|
404
|
+
startSigning(cohortId: string, txData: SigningTxData): BaseMessage[] {
|
|
405
|
+
const state = this.#cohortStates.get(cohortId);
|
|
406
|
+
if(!state) {
|
|
407
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
408
|
+
}
|
|
409
|
+
if(state.phase !== ServiceCohortPhase.Validated) {
|
|
410
|
+
throw new AggregationServiceError(
|
|
411
|
+
`Cannot start signing for cohort ${cohortId}: phase is ${state.phase}.`,
|
|
412
|
+
'INVALID_PHASE', { cohortId, phase: state.phase }
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const session = new BeaconSigningSession({
|
|
417
|
+
cohort : state.cohort,
|
|
418
|
+
pendingTx : txData.tx,
|
|
419
|
+
prevOutScripts : txData.prevOutScripts,
|
|
420
|
+
prevOutValues : txData.prevOutValues,
|
|
421
|
+
});
|
|
422
|
+
state.signingSession = session;
|
|
423
|
+
state.phase = ServiceCohortPhase.SigningStarted;
|
|
424
|
+
|
|
425
|
+
const messages: BaseMessage[] = [];
|
|
426
|
+
for(const participantDid of state.cohort.participants) {
|
|
427
|
+
messages.push(createAuthorizationRequestMessage({
|
|
428
|
+
from : this.did,
|
|
429
|
+
to : participantDid,
|
|
430
|
+
cohortId,
|
|
431
|
+
sessionId : session.id,
|
|
432
|
+
pendingTx : txData.tx.toHex(),
|
|
433
|
+
prevOutValue : txData.prevOutValues[0]?.toString() ?? '0',
|
|
434
|
+
}));
|
|
435
|
+
}
|
|
436
|
+
return messages;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
#handleNonceContribution(message: BaseMessage): void {
|
|
440
|
+
const cohortId = message.body?.cohortId;
|
|
441
|
+
if(!cohortId) return;
|
|
442
|
+
const state = this.#cohortStates.get(cohortId);
|
|
443
|
+
if(!state || !state.signingSession) return;
|
|
444
|
+
if(state.phase !== ServiceCohortPhase.SigningStarted) return;
|
|
445
|
+
|
|
446
|
+
const sessionId = message.body?.sessionId;
|
|
447
|
+
if(sessionId !== state.signingSession.id) return;
|
|
448
|
+
|
|
449
|
+
const nonceContribution = message.body?.nonceContribution;
|
|
450
|
+
if(!nonceContribution) return;
|
|
451
|
+
|
|
452
|
+
state.signingSession.addNonceContribution(message.from, nonceContribution);
|
|
453
|
+
|
|
454
|
+
if(state.signingSession.nonceContributions.size === state.cohort.participants.length) {
|
|
455
|
+
state.phase = ServiceCohortPhase.NoncesCollected;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Generate the aggregated nonce and return messages to send to participants.
|
|
461
|
+
* Call after `validationProgress(cohortId).approved.size === total`.
|
|
462
|
+
*/
|
|
463
|
+
sendAggregatedNonce(cohortId: string): BaseMessage[] {
|
|
464
|
+
const state = this.#cohortStates.get(cohortId);
|
|
465
|
+
if(!state) {
|
|
466
|
+
throw new AggregationServiceError(`Cohort ${cohortId} not found.`, 'COHORT_NOT_FOUND', { cohortId });
|
|
467
|
+
}
|
|
468
|
+
if(state.phase !== ServiceCohortPhase.NoncesCollected || !state.signingSession) {
|
|
469
|
+
throw new AggregationServiceError(
|
|
470
|
+
`Cannot send aggregated nonce for cohort ${cohortId}: phase is ${state.phase}.`,
|
|
471
|
+
'INVALID_PHASE', { cohortId, phase: state.phase }
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const aggregatedNonce = state.signingSession.generateAggregatedNonce();
|
|
476
|
+
state.phase = ServiceCohortPhase.AwaitingPartialSigs;
|
|
477
|
+
|
|
478
|
+
const messages: BaseMessage[] = [];
|
|
479
|
+
for(const participantDid of state.cohort.participants) {
|
|
480
|
+
messages.push(createAggregatedNonceMessage({
|
|
481
|
+
from : this.did,
|
|
482
|
+
to : participantDid,
|
|
483
|
+
cohortId,
|
|
484
|
+
sessionId : state.signingSession.id,
|
|
485
|
+
aggregatedNonce,
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
return messages;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
#handleSignatureAuthorization(message: BaseMessage): void {
|
|
492
|
+
const cohortId = message.body?.cohortId;
|
|
493
|
+
if(!cohortId) return;
|
|
494
|
+
const state = this.#cohortStates.get(cohortId);
|
|
495
|
+
if(!state || !state.signingSession) return;
|
|
496
|
+
if(state.phase !== ServiceCohortPhase.AwaitingPartialSigs) return;
|
|
497
|
+
|
|
498
|
+
const sessionId = message.body?.sessionId;
|
|
499
|
+
if(sessionId !== state.signingSession.id) return;
|
|
500
|
+
|
|
501
|
+
const partialSignature = message.body?.partialSignature;
|
|
502
|
+
if(!partialSignature) return;
|
|
503
|
+
|
|
504
|
+
state.signingSession.addPartialSignature(message.from, partialSignature);
|
|
505
|
+
|
|
506
|
+
if(state.signingSession.partialSignatures.size === state.cohort.participants.length) {
|
|
507
|
+
// All partial sigs received — generate final signature
|
|
508
|
+
const signature = state.signingSession.generateFinalSignature();
|
|
509
|
+
|
|
510
|
+
// Set Taproot key-path witness
|
|
511
|
+
state.signingSession.pendingTx.setWitness(0, [signature]);
|
|
512
|
+
|
|
513
|
+
state.result = {
|
|
514
|
+
cohortId,
|
|
515
|
+
signature,
|
|
516
|
+
signedTx : state.signingSession.pendingTx,
|
|
517
|
+
};
|
|
518
|
+
state.phase = ServiceCohortPhase.Complete;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
getResult(cohortId: string): AggregationResult | undefined {
|
|
524
|
+
return this.#cohortStates.get(cohortId)?.result;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
getCohortPhase(cohortId: string): ServiceCohortPhaseType | undefined {
|
|
528
|
+
return this.#cohortStates.get(cohortId)?.phase;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
getCohort(cohortId: string): AggregationCohort | undefined {
|
|
532
|
+
return this.#cohortStates.get(cohortId)?.cohort;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Get the signing session ID for a cohort, if a signing session has been started.
|
|
537
|
+
* @param {string} cohortId - The cohort ID.
|
|
538
|
+
* @returns {string | undefined} The session ID, or undefined if no session is active.
|
|
539
|
+
*/
|
|
540
|
+
getSigningSessionId(cohortId: string): string | undefined {
|
|
541
|
+
return this.#cohortStates.get(cohortId)?.signingSession?.id;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
get cohorts(): ReadonlyArray<AggregationCohort> {
|
|
545
|
+
return [...this.#cohortStates.values()].map(s => s.cohort);
|
|
546
|
+
}
|
|
547
|
+
}
|