@did-btcr2/aggregation 0.1.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/LICENSE +373 -0
- package/README.md +19 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/browser.js +24017 -0
- package/dist/browser.mjs +24011 -0
- package/dist/cjs/index.js +5690 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/beacon-strategy.js +76 -0
- package/dist/esm/beacon-strategy.js.map +1 -0
- package/dist/esm/cohort.js +316 -0
- package/dist/esm/cohort.js.map +1 -0
- package/dist/esm/conditions.js +102 -0
- package/dist/esm/conditions.js.map +1 -0
- package/dist/esm/errors.js +22 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/fallback-spend.js +132 -0
- package/dist/esm/fallback-spend.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger.js +15 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/messages/base.js +36 -0
- package/dist/esm/messages/base.js.map +1 -0
- package/dist/esm/messages/bodies.js +149 -0
- package/dist/esm/messages/bodies.js.map +1 -0
- package/dist/esm/messages/constants.js +37 -0
- package/dist/esm/messages/constants.js.map +1 -0
- package/dist/esm/messages/factories.js +139 -0
- package/dist/esm/messages/factories.js.map +1 -0
- package/dist/esm/messages/guards.js +40 -0
- package/dist/esm/messages/guards.js.map +1 -0
- package/dist/esm/messages/index.js +6 -0
- package/dist/esm/messages/index.js.map +1 -0
- package/dist/esm/participant.js +588 -0
- package/dist/esm/participant.js.map +1 -0
- package/dist/esm/phases.js +45 -0
- package/dist/esm/phases.js.map +1 -0
- package/dist/esm/recovery-policy.js +156 -0
- package/dist/esm/recovery-policy.js.map +1 -0
- package/dist/esm/recovery-spend.js +106 -0
- package/dist/esm/recovery-spend.js.map +1 -0
- package/dist/esm/runner/aggregation-runner.js +75 -0
- package/dist/esm/runner/aggregation-runner.js.map +1 -0
- package/dist/esm/runner/events.js +2 -0
- package/dist/esm/runner/events.js.map +1 -0
- package/dist/esm/runner/index.js +6 -0
- package/dist/esm/runner/index.js.map +1 -0
- package/dist/esm/runner/participant-runner.js +407 -0
- package/dist/esm/runner/participant-runner.js.map +1 -0
- package/dist/esm/runner/service-runner.js +701 -0
- package/dist/esm/runner/service-runner.js.map +1 -0
- package/dist/esm/runner/typed-emitter.js +80 -0
- package/dist/esm/runner/typed-emitter.js.map +1 -0
- package/dist/esm/service.js +774 -0
- package/dist/esm/service.js.map +1 -0
- package/dist/esm/signer.js +33 -0
- package/dist/esm/signer.js.map +1 -0
- package/dist/esm/signing-session.js +195 -0
- package/dist/esm/signing-session.js.map +1 -0
- package/dist/esm/transport/didcomm.js +41 -0
- package/dist/esm/transport/didcomm.js.map +1 -0
- package/dist/esm/transport/error.js +12 -0
- package/dist/esm/transport/error.js.map +1 -0
- package/dist/esm/transport/factory.js +29 -0
- package/dist/esm/transport/factory.js.map +1 -0
- package/dist/esm/transport/http/client.js +342 -0
- package/dist/esm/transport/http/client.js.map +1 -0
- package/dist/esm/transport/http/envelope.js +126 -0
- package/dist/esm/transport/http/envelope.js.map +1 -0
- package/dist/esm/transport/http/errors.js +11 -0
- package/dist/esm/transport/http/errors.js.map +1 -0
- package/dist/esm/transport/http/inbox-buffer.js +45 -0
- package/dist/esm/transport/http/inbox-buffer.js.map +1 -0
- package/dist/esm/transport/http/index.js +12 -0
- package/dist/esm/transport/http/index.js.map +1 -0
- package/dist/esm/transport/http/nonce-cache.js +38 -0
- package/dist/esm/transport/http/nonce-cache.js.map +1 -0
- package/dist/esm/transport/http/protocol.js +28 -0
- package/dist/esm/transport/http/protocol.js.map +1 -0
- package/dist/esm/transport/http/rate-limiter.js +45 -0
- package/dist/esm/transport/http/rate-limiter.js.map +1 -0
- package/dist/esm/transport/http/request-auth.js +100 -0
- package/dist/esm/transport/http/request-auth.js.map +1 -0
- package/dist/esm/transport/http/server.js +475 -0
- package/dist/esm/transport/http/server.js.map +1 -0
- package/dist/esm/transport/http/sse-stream.js +110 -0
- package/dist/esm/transport/http/sse-stream.js.map +1 -0
- package/dist/esm/transport/http/sse-writer.js +25 -0
- package/dist/esm/transport/http/sse-writer.js.map +1 -0
- package/dist/esm/transport/in-memory.js +149 -0
- package/dist/esm/transport/in-memory.js.map +1 -0
- package/dist/esm/transport/index.js +8 -0
- package/dist/esm/transport/index.js.map +1 -0
- package/dist/esm/transport/nostr.js +491 -0
- package/dist/esm/transport/nostr.js.map +1 -0
- package/dist/esm/transport/transport.js +2 -0
- package/dist/esm/transport/transport.js.map +1 -0
- package/dist/types/beacon-strategy.d.ts +56 -0
- package/dist/types/beacon-strategy.d.ts.map +1 -0
- package/dist/types/cohort.d.ts +188 -0
- package/dist/types/cohort.d.ts.map +1 -0
- package/dist/types/conditions.d.ts +86 -0
- package/dist/types/conditions.d.ts.map +1 -0
- package/dist/types/errors.d.ts +14 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/fallback-spend.d.ts +94 -0
- package/dist/types/fallback-spend.d.ts.map +1 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/logger.d.ts +22 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/messages/base.d.ts +58 -0
- package/dist/types/messages/base.d.ts.map +1 -0
- package/dist/types/messages/bodies.d.ts +177 -0
- package/dist/types/messages/bodies.d.ts.map +1 -0
- package/dist/types/messages/constants.d.ts +31 -0
- package/dist/types/messages/constants.d.ts.map +1 -0
- package/dist/types/messages/factories.d.ts +217 -0
- package/dist/types/messages/factories.d.ts.map +1 -0
- package/dist/types/messages/guards.d.ts +11 -0
- package/dist/types/messages/guards.d.ts.map +1 -0
- package/dist/types/messages/index.d.ts +6 -0
- package/dist/types/messages/index.d.ts.map +1 -0
- package/dist/types/participant.d.ts +162 -0
- package/dist/types/participant.d.ts.map +1 -0
- package/dist/types/phases.d.ts +55 -0
- package/dist/types/phases.d.ts.map +1 -0
- package/dist/types/recovery-policy.d.ts +132 -0
- package/dist/types/recovery-policy.d.ts.map +1 -0
- package/dist/types/recovery-spend.d.ts +94 -0
- package/dist/types/recovery-spend.d.ts.map +1 -0
- package/dist/types/runner/aggregation-runner.d.ts +72 -0
- package/dist/types/runner/aggregation-runner.d.ts.map +1 -0
- package/dist/types/runner/events.d.ts +147 -0
- package/dist/types/runner/events.d.ts.map +1 -0
- package/dist/types/runner/index.d.ts +6 -0
- package/dist/types/runner/index.d.ts.map +1 -0
- package/dist/types/runner/participant-runner.d.ts +122 -0
- package/dist/types/runner/participant-runner.d.ts.map +1 -0
- package/dist/types/runner/service-runner.d.ts +228 -0
- package/dist/types/runner/service-runner.d.ts.map +1 -0
- package/dist/types/runner/typed-emitter.d.ts +41 -0
- package/dist/types/runner/typed-emitter.d.ts.map +1 -0
- package/dist/types/service.d.ts +177 -0
- package/dist/types/service.d.ts.map +1 -0
- package/dist/types/signer.d.ts +50 -0
- package/dist/types/signer.d.ts.map +1 -0
- package/dist/types/signing-session.d.ts +81 -0
- package/dist/types/signing-session.d.ts.map +1 -0
- package/dist/types/transport/didcomm.d.ts +23 -0
- package/dist/types/transport/didcomm.d.ts.map +1 -0
- package/dist/types/transport/error.d.ts +8 -0
- package/dist/types/transport/error.d.ts.map +1 -0
- package/dist/types/transport/factory.d.ts +28 -0
- package/dist/types/transport/factory.d.ts.map +1 -0
- package/dist/types/transport/http/client.d.ts +56 -0
- package/dist/types/transport/http/client.d.ts.map +1 -0
- package/dist/types/transport/http/envelope.d.ts +64 -0
- package/dist/types/transport/http/envelope.d.ts.map +1 -0
- package/dist/types/transport/http/errors.d.ts +9 -0
- package/dist/types/transport/http/errors.d.ts.map +1 -0
- package/dist/types/transport/http/inbox-buffer.d.ts +32 -0
- package/dist/types/transport/http/inbox-buffer.d.ts.map +1 -0
- package/dist/types/transport/http/index.d.ts +12 -0
- package/dist/types/transport/http/index.d.ts.map +1 -0
- package/dist/types/transport/http/nonce-cache.d.ts +26 -0
- package/dist/types/transport/http/nonce-cache.d.ts.map +1 -0
- package/dist/types/transport/http/protocol.d.ts +53 -0
- package/dist/types/transport/http/protocol.d.ts.map +1 -0
- package/dist/types/transport/http/rate-limiter.d.ts +41 -0
- package/dist/types/transport/http/rate-limiter.d.ts.map +1 -0
- package/dist/types/transport/http/request-auth.d.ts +50 -0
- package/dist/types/transport/http/request-auth.d.ts.map +1 -0
- package/dist/types/transport/http/server.d.ts +118 -0
- package/dist/types/transport/http/server.d.ts.map +1 -0
- package/dist/types/transport/http/sse-stream.d.ts +34 -0
- package/dist/types/transport/http/sse-stream.d.ts.map +1 -0
- package/dist/types/transport/http/sse-writer.d.ts +12 -0
- package/dist/types/transport/http/sse-writer.d.ts.map +1 -0
- package/dist/types/transport/in-memory.d.ts +64 -0
- package/dist/types/transport/in-memory.d.ts.map +1 -0
- package/dist/types/transport/index.d.ts +8 -0
- package/dist/types/transport/index.d.ts.map +1 -0
- package/dist/types/transport/nostr.d.ts +153 -0
- package/dist/types/transport/nostr.d.ts.map +1 -0
- package/dist/types/transport/transport.d.ts +62 -0
- package/dist/types/transport/transport.d.ts.map +1 -0
- package/package.json +128 -0
- package/src/beacon-strategy.ts +142 -0
- package/src/cohort.ts +422 -0
- package/src/conditions.ts +172 -0
- package/src/errors.ts +25 -0
- package/src/fallback-spend.ts +223 -0
- package/src/index.ts +23 -0
- package/src/logger.ts +33 -0
- package/src/messages/base.ts +80 -0
- package/src/messages/bodies.ts +328 -0
- package/src/messages/constants.ts +40 -0
- package/src/messages/factories.ts +297 -0
- package/src/messages/guards.ts +61 -0
- package/src/messages/index.ts +5 -0
- package/src/participant.ts +779 -0
- package/src/phases.ts +91 -0
- package/src/recovery-policy.ts +237 -0
- package/src/recovery-spend.ts +192 -0
- package/src/runner/aggregation-runner.ts +125 -0
- package/src/runner/events.ts +129 -0
- package/src/runner/index.ts +5 -0
- package/src/runner/participant-runner.ts +488 -0
- package/src/runner/service-runner.ts +891 -0
- package/src/runner/typed-emitter.ts +87 -0
- package/src/service.ts +1000 -0
- package/src/signer.ts +67 -0
- package/src/signing-session.ts +290 -0
- package/src/transport/didcomm.ts +59 -0
- package/src/transport/error.ts +13 -0
- package/src/transport/factory.ts +65 -0
- package/src/transport/http/client.ts +409 -0
- package/src/transport/http/envelope.ts +204 -0
- package/src/transport/http/errors.ts +11 -0
- package/src/transport/http/inbox-buffer.ts +53 -0
- package/src/transport/http/index.ts +11 -0
- package/src/transport/http/nonce-cache.ts +43 -0
- package/src/transport/http/protocol.ts +57 -0
- package/src/transport/http/rate-limiter.ts +75 -0
- package/src/transport/http/request-auth.ts +164 -0
- package/src/transport/http/server.ts +617 -0
- package/src/transport/http/sse-stream.ts +121 -0
- package/src/transport/http/sse-writer.ts +23 -0
- package/src/transport/in-memory.ts +178 -0
- package/src/transport/index.ts +7 -0
- package/src/transport/nostr.ts +576 -0
- package/src/transport/transport.ts +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "commonjs"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { canonicalize } from '@did-btcr2/common';
|
|
2
|
+
import { base64UrlToHash, blockHash, didToIndex, hashToBase64Url, verifySerializedProof } from '@did-btcr2/smt';
|
|
3
|
+
const CAS_STRATEGY = {
|
|
4
|
+
type: 'CASBeacon',
|
|
5
|
+
buildAggregatedData(cohort) {
|
|
6
|
+
cohort.buildCASAnnouncement();
|
|
7
|
+
},
|
|
8
|
+
getDistributePayload(cohort) {
|
|
9
|
+
return { casAnnouncement: cohort.casAnnouncement };
|
|
10
|
+
},
|
|
11
|
+
validateParticipantView({ participantDid, included, expectedHash, body }) {
|
|
12
|
+
const casAnnouncement = body.casAnnouncement;
|
|
13
|
+
if (!casAnnouncement)
|
|
14
|
+
return { matches: false };
|
|
15
|
+
if (!included) {
|
|
16
|
+
// Cooperative non-inclusion: the decliner must be ABSENT from the map.
|
|
17
|
+
return { matches: !(participantDid in casAnnouncement), casAnnouncement };
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
matches: casAnnouncement[participantDid] === expectedHash,
|
|
21
|
+
casAnnouncement,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const SMT_STRATEGY = {
|
|
26
|
+
type: 'SMTBeacon',
|
|
27
|
+
buildAggregatedData(cohort) {
|
|
28
|
+
cohort.buildSMTTree();
|
|
29
|
+
},
|
|
30
|
+
getDistributePayload(cohort, participantDid) {
|
|
31
|
+
const proof = cohort.smtProofs?.get(participantDid);
|
|
32
|
+
return { smtProof: proof };
|
|
33
|
+
},
|
|
34
|
+
validateParticipantView({ participantDid, included, submittedUpdate, body }) {
|
|
35
|
+
const smtProof = body.smtProof;
|
|
36
|
+
const index = didToIndex(participantDid);
|
|
37
|
+
if (!included) {
|
|
38
|
+
// Cooperative non-inclusion: the proof has a nonce but no updateId by
|
|
39
|
+
// construction. The leaf is SHA-256(SHA-256(nonce)); do NOT run the
|
|
40
|
+
// inclusion-only updateId guard (a missing updateId is correct here).
|
|
41
|
+
if (!smtProof?.nonce || smtProof?.updateId)
|
|
42
|
+
return { matches: false, smtProof };
|
|
43
|
+
const candidateHash = blockHash(blockHash(base64UrlToHash(smtProof.nonce)));
|
|
44
|
+
return { matches: verifySerializedProof(smtProof, index, candidateHash), smtProof };
|
|
45
|
+
}
|
|
46
|
+
if (!smtProof?.updateId || !smtProof?.nonce || !submittedUpdate)
|
|
47
|
+
return { matches: false, smtProof };
|
|
48
|
+
// Verify updateId matches the canonicalized update hash. Proof hash fields
|
|
49
|
+
// are base64url (no padding) per the SMT Proof spec.
|
|
50
|
+
const canonicalBytes = new TextEncoder().encode(canonicalize(submittedUpdate));
|
|
51
|
+
const expectedUpdateId = hashToBase64Url(blockHash(canonicalBytes));
|
|
52
|
+
if (smtProof.updateId !== expectedUpdateId) {
|
|
53
|
+
return { matches: false, smtProof };
|
|
54
|
+
}
|
|
55
|
+
// Verify Merkle inclusion. The leaf is SHA-256(SHA-256(nonce) || SHA-256(update)).
|
|
56
|
+
const candidateHash = blockHash(blockHash(base64UrlToHash(smtProof.nonce)), base64UrlToHash(smtProof.updateId));
|
|
57
|
+
return {
|
|
58
|
+
matches: verifySerializedProof(smtProof, index, candidateHash),
|
|
59
|
+
smtProof,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
/** Registered strategies keyed by `beaconType` string. */
|
|
64
|
+
const STRATEGIES = new Map([
|
|
65
|
+
[CAS_STRATEGY.type, CAS_STRATEGY],
|
|
66
|
+
[SMT_STRATEGY.type, SMT_STRATEGY],
|
|
67
|
+
]);
|
|
68
|
+
/** Register a custom beacon strategy. Overwrites any existing entry with the same type. */
|
|
69
|
+
export function registerBeaconStrategy(strategy) {
|
|
70
|
+
STRATEGIES.set(strategy.type, strategy);
|
|
71
|
+
}
|
|
72
|
+
/** Look up a registered beacon strategy by type, or undefined if not registered. */
|
|
73
|
+
export function getBeaconStrategy(type) {
|
|
74
|
+
return STRATEGIES.get(type);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=beacon-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"beacon-strategy.js","sourceRoot":"","sources":["../../src/beacon-strategy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { getNetwork } from '@did-btcr2/bitcoin';
|
|
2
|
+
import { canonicalHash, canonicalize, hash } from '@did-btcr2/common';
|
|
3
|
+
import { BTCR2MerkleTree } from '@did-btcr2/smt';
|
|
4
|
+
import { schnorr } from '@noble/curves/secp256k1.js';
|
|
5
|
+
import { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils';
|
|
6
|
+
import { p2tr } from '@scure/btc-signer';
|
|
7
|
+
import { keyAggExport, keyAggregate, sortKeys } from '@scure/btc-signer/musig2';
|
|
8
|
+
import { AggregationCohortError } from './errors.js';
|
|
9
|
+
import { DEFAULT_FUNDING_MODEL, buildRecoveryLeaves, resolveFallbackThreshold } from './recovery-policy.js';
|
|
10
|
+
/**
|
|
11
|
+
* Represents an Aggregation Cohort: a set of Aggregation Participants who
|
|
12
|
+
* submitted cryptographic material to an Aggregation Service to coordinate
|
|
13
|
+
* signing of a shared n-of-n MuSig2 Bitcoin transaction.
|
|
14
|
+
*
|
|
15
|
+
* This is a pure data class: it holds cohort state and provides computation
|
|
16
|
+
* helpers (key aggregation, CAS Announcement building, SMT tree building).
|
|
17
|
+
* It performs no I/O and emits no messages. Both AggregationService and
|
|
18
|
+
* AggregationParticipant create their own AggregationCohort instances to
|
|
19
|
+
* track their respective views of the cohort state.
|
|
20
|
+
*
|
|
21
|
+
* @class AggregationCohort
|
|
22
|
+
*/
|
|
23
|
+
export class AggregationCohort {
|
|
24
|
+
/** Unique identifier for the cohort. */
|
|
25
|
+
id;
|
|
26
|
+
/** DID of the Aggregation Service managing this cohort. */
|
|
27
|
+
serviceDid;
|
|
28
|
+
/** Minimum number of participants required to finalize the cohort. */
|
|
29
|
+
minParticipants;
|
|
30
|
+
/** Network on which the cohort operates (mainnet, mutinynet, etc.). */
|
|
31
|
+
network;
|
|
32
|
+
/** Type of beacon used in the cohort: 'CASBeacon' or 'SMTBeacon'. */
|
|
33
|
+
beaconType;
|
|
34
|
+
/** List of participant DIDs that have been accepted into the cohort. */
|
|
35
|
+
participants = [];
|
|
36
|
+
/**
|
|
37
|
+
* Mapping from participant DID to their compressed secp256k1 public key.
|
|
38
|
+
* Distinct from {@link cohortKeys} (which is sorted per BIP-327): this lets
|
|
39
|
+
* callers look up a participant's key without knowing their position in the
|
|
40
|
+
* sorted array. Populated by the service at `acceptParticipant` time.
|
|
41
|
+
*/
|
|
42
|
+
participantKeys = new Map();
|
|
43
|
+
/** Sorted list of cohort participants' compressed public keys. */
|
|
44
|
+
#cohortKeys = [];
|
|
45
|
+
/**
|
|
46
|
+
* BIP-341 TapTweak scalar: `taggedHash("TapTweak", internalPubkey || tapMerkleRoot)`.
|
|
47
|
+
* The beacon output is an internal key (the MuSig2 aggregate) plus a script
|
|
48
|
+
* tree (the recovery leaves), so the tweak commits to the tree's Merkle root.
|
|
49
|
+
* The MuSig2 signing session applies this as an x-only tweak; a value that does
|
|
50
|
+
* not match the root the funded address was derived from silently yields an
|
|
51
|
+
* invalid key-path signature.
|
|
52
|
+
*/
|
|
53
|
+
tapTweak = new Uint8Array();
|
|
54
|
+
/** The n-of-n MuSig2 aggregate internal key, x-only (32 bytes), set by computeBeaconAddress(). */
|
|
55
|
+
internalKey = new Uint8Array();
|
|
56
|
+
/** BIP-341 Taproot Merkle root of the recovery script tree, set by computeBeaconAddress(). */
|
|
57
|
+
tapMerkleRoot = new Uint8Array();
|
|
58
|
+
/** Operator recovery key, x-only (32 bytes). Used to build the CSV recovery leaf. */
|
|
59
|
+
recoveryKey;
|
|
60
|
+
/** Relative-timelock (BIP-68 nSequence) before the recovery leaf is spendable. */
|
|
61
|
+
recoverySequence;
|
|
62
|
+
/** Funding model governing the recovery leaves (default 'operator-funded'). */
|
|
63
|
+
fundingModel;
|
|
64
|
+
/**
|
|
65
|
+
* Advertised k of the k-of-n fallback leaf, or undefined to default to n-1 at
|
|
66
|
+
* address computation. Read the resolved value via {@link effectiveFallbackThreshold}.
|
|
67
|
+
*/
|
|
68
|
+
fallbackThreshold;
|
|
69
|
+
/** The Taproot beacon address: key path is the MuSig2 aggregate, script path is fallback + recovery. */
|
|
70
|
+
beaconAddress = '';
|
|
71
|
+
/** Pending DID updates submitted by participants, keyed by DID. */
|
|
72
|
+
pendingUpdates = new Map();
|
|
73
|
+
/**
|
|
74
|
+
* Participant DIDs that explicitly declined to submit an update this round
|
|
75
|
+
* (cooperative non-inclusion). A decliner is absent from the CAS Announcement
|
|
76
|
+
* Map and carries a non-inclusion leaf in the SMT, yet still signs. Kept
|
|
77
|
+
* disjoint from {@link pendingUpdates} so CAS correctness holds by construction.
|
|
78
|
+
*/
|
|
79
|
+
nonIncluded = new Set();
|
|
80
|
+
/** CAS Beacon Announcement Map (DID to updateHash), set by buildCASAnnouncement(). */
|
|
81
|
+
casAnnouncement;
|
|
82
|
+
/** Per-participant SMT proofs, set by buildSMTTree(). */
|
|
83
|
+
smtProofs;
|
|
84
|
+
/** Signal bytes (32 bytes) for OP_RETURN: SHA-256 of CAS announcement OR SMT root. */
|
|
85
|
+
signalBytes;
|
|
86
|
+
/** Set of participant DIDs that have approved the aggregated data. */
|
|
87
|
+
validationAcks = new Set();
|
|
88
|
+
/** Set of participant DIDs that have rejected the aggregated data. */
|
|
89
|
+
validationRejections = new Set();
|
|
90
|
+
constructor({ id, minParticipants, serviceDid, network, beaconType, recoveryKey, recoverySequence, fundingModel, fallbackThreshold }) {
|
|
91
|
+
this.id = id || crypto.randomUUID();
|
|
92
|
+
// `?? 2` (not `|| 2`) so a deliberately-passed 0 is preserved rather than
|
|
93
|
+
// silently coerced; the service rejects an invalid count at createCohort.
|
|
94
|
+
this.minParticipants = minParticipants ?? 2;
|
|
95
|
+
this.serviceDid = serviceDid || '';
|
|
96
|
+
this.network = network;
|
|
97
|
+
this.beaconType = beaconType || 'CASBeacon';
|
|
98
|
+
this.recoveryKey = recoveryKey;
|
|
99
|
+
this.recoverySequence = recoverySequence;
|
|
100
|
+
this.fundingModel = fundingModel ?? DEFAULT_FUNDING_MODEL;
|
|
101
|
+
this.fallbackThreshold = fallbackThreshold;
|
|
102
|
+
}
|
|
103
|
+
/** Sorted cohort keys (sorted on assignment per BIP-327). */
|
|
104
|
+
get cohortKeys() {
|
|
105
|
+
return this.#cohortKeys;
|
|
106
|
+
}
|
|
107
|
+
set cohortKeys(keys) {
|
|
108
|
+
this.#cohortKeys = sortKeys(keys);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The resolved k of the k-of-n fallback leaf: the advertised
|
|
112
|
+
* {@link fallbackThreshold}, or n-1 (floored at 1) when unadvertised, where n is
|
|
113
|
+
* the current cohort size. This is the value the beacon address commits to and
|
|
114
|
+
* the spend builders must reproduce. Returns 0 before any cohort keys are set.
|
|
115
|
+
*/
|
|
116
|
+
get effectiveFallbackThreshold() {
|
|
117
|
+
if (this.#cohortKeys.length === 0)
|
|
118
|
+
return 0;
|
|
119
|
+
return resolveFallbackThreshold(this.fallbackThreshold, this.#cohortKeys.length);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Computes the Taproot beacon address from the cohort keys and recovery params.
|
|
123
|
+
*
|
|
124
|
+
* The output's key path is the n-of-n MuSig2 aggregate of the cohort keys; its
|
|
125
|
+
* script path is the recovery tree (a CSV recovery leaf so a missing signer
|
|
126
|
+
* cannot permanently lock the UTXO). Sets `internalKey`, `tapMerkleRoot`, and
|
|
127
|
+
* the `tapTweak` the MuSig2 session must apply for the key-path spend.
|
|
128
|
+
*
|
|
129
|
+
* The tweak is derived from the Merkle root the address was built with (read
|
|
130
|
+
* back from the payment), never recomputed by hand, so the MuSig2 key-path
|
|
131
|
+
* signature is guaranteed to validate against the funded address.
|
|
132
|
+
*/
|
|
133
|
+
computeBeaconAddress() {
|
|
134
|
+
if (this.#cohortKeys.length === 0) {
|
|
135
|
+
throw new AggregationCohortError('Cannot compute beacon address: no cohort keys.', 'NO_COHORT_KEYS', { cohortId: this.id });
|
|
136
|
+
}
|
|
137
|
+
if (!this.recoveryKey || this.recoveryKey.length === 0 || this.recoverySequence === undefined) {
|
|
138
|
+
throw new AggregationCohortError('Cannot compute beacon address: missing recovery key or sequence.', 'NO_RECOVERY_PARAMS', { cohortId: this.id });
|
|
139
|
+
}
|
|
140
|
+
const keyAggContext = keyAggregate(this.#cohortKeys);
|
|
141
|
+
const aggPubkey = keyAggExport(keyAggContext);
|
|
142
|
+
// The beacon output commits to the script tree (k-of-n fallback leaf + CSV
|
|
143
|
+
// recovery leaf) alongside the MuSig2 internal key. Derive the address for
|
|
144
|
+
// the cohort's network. Without the network arg p2tr defaults to mainnet, so
|
|
145
|
+
// a mutinynet/signet/regtest cohort would otherwise advertise a `bc1p...`
|
|
146
|
+
// address that no participant can fund.
|
|
147
|
+
const leaves = buildRecoveryLeaves(this.fundingModel, {
|
|
148
|
+
recoveryKey: this.recoveryKey,
|
|
149
|
+
recoverySequence: this.recoverySequence,
|
|
150
|
+
cohortKeys: this.#cohortKeys,
|
|
151
|
+
fallbackThreshold: resolveFallbackThreshold(this.fallbackThreshold, this.#cohortKeys.length),
|
|
152
|
+
});
|
|
153
|
+
// allowUnknownOutputs: the CSV recovery leaf is a custom script, not one of
|
|
154
|
+
// p2tr's recognized templates (tr_ns/tr_ms), so the template check must be
|
|
155
|
+
// waived. The leaf hash (and thus the address) is computed from the raw
|
|
156
|
+
// script bytes regardless.
|
|
157
|
+
const payment = p2tr(aggPubkey, leaves, getNetwork(this.network), true);
|
|
158
|
+
// BIP-341: the key-path tweak commits to the script tree's Merkle root.
|
|
159
|
+
// taggedHash("TapTweak", internalPubkey || tapMerkleRoot). Use the root the
|
|
160
|
+
// library committed to (payment.tapMerkleRoot) so the tweak matches the
|
|
161
|
+
// funded address byte-for-byte.
|
|
162
|
+
this.internalKey = aggPubkey;
|
|
163
|
+
this.tapMerkleRoot = payment.tapMerkleRoot;
|
|
164
|
+
this.tapTweak = schnorr.utils.taggedHash('TapTweak', concatBytes(aggPubkey, payment.tapMerkleRoot));
|
|
165
|
+
if (!payment.address) {
|
|
166
|
+
throw new AggregationCohortError('Failed to compute Taproot address', 'BEACON_ADDRESS_ERROR', { cohortId: this.id });
|
|
167
|
+
}
|
|
168
|
+
this.beaconAddress = payment.address;
|
|
169
|
+
return payment.address;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Validates that the participant's key is in the cohort and the beacon address
|
|
173
|
+
* matches the locally-computed one. Used by participants to verify cohort ready
|
|
174
|
+
* messages from the service.
|
|
175
|
+
*/
|
|
176
|
+
validateMembership(participantPkHex, cohortKeysHex, expectedBeaconAddress) {
|
|
177
|
+
if (!cohortKeysHex.includes(participantPkHex)) {
|
|
178
|
+
throw new AggregationCohortError(`Participant key not found in cohort ${this.id}.`, 'COHORT_VALIDATION_ERROR', { cohortId: this.id, participantPkHex });
|
|
179
|
+
}
|
|
180
|
+
this.cohortKeys = cohortKeysHex.map(k => hexToBytes(k));
|
|
181
|
+
const computed = this.computeBeaconAddress();
|
|
182
|
+
if (computed !== expectedBeaconAddress) {
|
|
183
|
+
throw new AggregationCohortError(`Computed beacon address ${computed} does not match expected ${expectedBeaconAddress}.`, 'BEACON_ADDRESS_MISMATCH', { cohortId: this.id, computed, expected: expectedBeaconAddress });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Returns the position of a participant's public key in the sorted
|
|
188
|
+
* {@link cohortKeys} array, or -1 if the participant is not in the cohort.
|
|
189
|
+
* Required by MuSig2 partial-sig verification which indexes by signer position.
|
|
190
|
+
*/
|
|
191
|
+
indexOfParticipant(did) {
|
|
192
|
+
const pk = this.participantKeys.get(did);
|
|
193
|
+
if (!pk)
|
|
194
|
+
return -1;
|
|
195
|
+
return this.#cohortKeys.findIndex(k => k.length === pk.length && k.every((b, i) => b === pk[i]));
|
|
196
|
+
}
|
|
197
|
+
addUpdate(participantDid, signedUpdate) {
|
|
198
|
+
if (!this.participants.includes(participantDid)) {
|
|
199
|
+
throw new AggregationCohortError(`Participant ${participantDid} is not in cohort ${this.id}.`, 'UNKNOWN_PARTICIPANT', { cohortId: this.id, participantDid });
|
|
200
|
+
}
|
|
201
|
+
this.pendingUpdates.set(participantDid, signedUpdate);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Record that a participant declined to submit an update this round
|
|
205
|
+
* (cooperative non-inclusion). The member stays in the cohort and still signs.
|
|
206
|
+
*/
|
|
207
|
+
addNonInclusion(participantDid) {
|
|
208
|
+
if (!this.participants.includes(participantDid)) {
|
|
209
|
+
throw new AggregationCohortError(`Participant ${participantDid} is not in cohort ${this.id}.`, 'UNKNOWN_PARTICIPANT', { cohortId: this.id, participantDid });
|
|
210
|
+
}
|
|
211
|
+
// A DID cannot both submit and decline. The service guards re-submission, but
|
|
212
|
+
// keep the invariant local so the response gate and builders stay consistent.
|
|
213
|
+
if (this.pendingUpdates.has(participantDid)) {
|
|
214
|
+
throw new AggregationCohortError(`Participant ${participantDid} already submitted an update; cannot also decline.`, 'CONFLICTING_RESPONSE', { cohortId: this.id, participantDid });
|
|
215
|
+
}
|
|
216
|
+
this.nonIncluded.add(participantDid);
|
|
217
|
+
}
|
|
218
|
+
hasAllUpdates() {
|
|
219
|
+
return this.pendingUpdates.size === this.participants.length;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* True when every participant has responded for this round, either with an
|
|
223
|
+
* update or with an explicit non-inclusion. This is the aggregation gate when
|
|
224
|
+
* non-inclusion is in play; it generalizes {@link hasAllUpdates} the same way
|
|
225
|
+
* {@link hasAllValidationResponses} generalizes a unanimous ack.
|
|
226
|
+
*/
|
|
227
|
+
hasAllResponses() {
|
|
228
|
+
return this.pendingUpdates.size + this.nonIncluded.size === this.participants.length;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Builds a CAS Announcement Map from collected updates.
|
|
232
|
+
* Maps each participant DID to base64url canonical hash of their signed update.
|
|
233
|
+
* Computes signal bytes as SHA-256 of canonicalized announcement.
|
|
234
|
+
*
|
|
235
|
+
* Members who declined (cooperative non-inclusion) are naturally absent from
|
|
236
|
+
* the map: the body iterates {@link pendingUpdates}, which never holds a
|
|
237
|
+
* decliner. Absence from the map is exactly the CAS non-inclusion signal.
|
|
238
|
+
*/
|
|
239
|
+
buildCASAnnouncement() {
|
|
240
|
+
if (!this.hasAllResponses()) {
|
|
241
|
+
throw new AggregationCohortError('Cannot build CAS Announcement: not all participants have responded.', 'INCOMPLETE_RESPONSES', { cohortId: this.id, updates: this.pendingUpdates.size, declined: this.nonIncluded.size, total: this.participants.length });
|
|
242
|
+
}
|
|
243
|
+
const announcement = {};
|
|
244
|
+
for (const [did, signedUpdate] of this.pendingUpdates) {
|
|
245
|
+
announcement[did] = canonicalHash(signedUpdate);
|
|
246
|
+
}
|
|
247
|
+
this.casAnnouncement = announcement;
|
|
248
|
+
this.signalBytes = hash(canonicalize(announcement));
|
|
249
|
+
return announcement;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Builds an SMT tree with one leaf per participant.
|
|
253
|
+
*
|
|
254
|
+
* A member who submitted an update gets an inclusion leaf
|
|
255
|
+
* (SHA-256(SHA-256(nonce) || SHA-256(update))); a member who declined gets a
|
|
256
|
+
* non-inclusion leaf (SHA-256(SHA-256(nonce)), the `signedUpdate` entry field
|
|
257
|
+
* omitted). The cohort mints each member's nonce and returns it inside that
|
|
258
|
+
* member's serialized proof, so a decliner can self-validate its own
|
|
259
|
+
* non-inclusion slot and the resolver can recompute the leaf. Stores
|
|
260
|
+
* per-participant proofs and the SMT root as signalBytes.
|
|
261
|
+
*/
|
|
262
|
+
buildSMTTree() {
|
|
263
|
+
if (!this.hasAllResponses()) {
|
|
264
|
+
throw new AggregationCohortError('Cannot build SMT tree: not all participants have responded.', 'INCOMPLETE_RESPONSES', { cohortId: this.id, updates: this.pendingUpdates.size, declined: this.nonIncluded.size, total: this.participants.length });
|
|
265
|
+
}
|
|
266
|
+
const tree = new BTCR2MerkleTree();
|
|
267
|
+
const entries = [];
|
|
268
|
+
const encoder = new TextEncoder();
|
|
269
|
+
// Slot every participant: an inclusion leaf for submitters, a non-inclusion
|
|
270
|
+
// leaf (signedUpdate omitted) for decliners.
|
|
271
|
+
for (const did of this.participants) {
|
|
272
|
+
const nonce = randomBytes(32);
|
|
273
|
+
const signedUpdate = this.pendingUpdates.get(did);
|
|
274
|
+
if (signedUpdate) {
|
|
275
|
+
entries.push({ did, nonce, signedUpdate: encoder.encode(canonicalize(signedUpdate)) });
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
entries.push({ did, nonce });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
tree.addEntries(entries);
|
|
282
|
+
tree.finalize();
|
|
283
|
+
this.signalBytes = tree.rootHash;
|
|
284
|
+
this.smtProofs = new Map();
|
|
285
|
+
for (const did of this.participants) {
|
|
286
|
+
this.smtProofs.set(did, tree.proof(did));
|
|
287
|
+
}
|
|
288
|
+
return this.smtProofs;
|
|
289
|
+
}
|
|
290
|
+
addValidation(participantDid, approved) {
|
|
291
|
+
if (!this.participants.includes(participantDid)) {
|
|
292
|
+
throw new AggregationCohortError(`Unknown participant ${participantDid} in cohort ${this.id}.`, 'UNKNOWN_PARTICIPANT', { cohortId: this.id, participantDid });
|
|
293
|
+
}
|
|
294
|
+
if (approved) {
|
|
295
|
+
this.validationAcks.add(participantDid);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
this.validationRejections.add(participantDid);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* True when every participant has either approved or rejected the aggregated data.
|
|
303
|
+
*/
|
|
304
|
+
hasAllValidationResponses() {
|
|
305
|
+
return this.validationAcks.size + this.validationRejections.size === this.participants.length;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* True when all participants approved. Note: differs from {@link hasAllValidationResponses},
|
|
309
|
+
* this returns false if any participant rejected, even if all responses are in.
|
|
310
|
+
*/
|
|
311
|
+
isFullyValidated() {
|
|
312
|
+
return this.validationRejections.size === 0
|
|
313
|
+
&& this.validationAcks.size === this.participants.length;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=cohort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cohort.js","sourceRoot":"","sources":["../../src/cohort.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort conditions: the constraints an Aggregation Service advertises for a
|
|
3
|
+
* cohort (did:btcr2 spec, "Step 1: Create Aggregation Cohort"). See ADR 039.
|
|
4
|
+
*
|
|
5
|
+
* The spec frames these as an optional menu ("the Aggregation Service can define
|
|
6
|
+
* conditions such as ..."), so only `beaconType` and `minParticipants` are
|
|
7
|
+
* required here; every other condition is optional and, when absent, means
|
|
8
|
+
* unconstrained.
|
|
9
|
+
*
|
|
10
|
+
* Enforcement is staged (ADR 039): `beaconType` and the participant bounds are
|
|
11
|
+
* enforced by the state machine now; DIDs-per-participant, timing/cadence, and
|
|
12
|
+
* the pending-update trigger are modeled and advertised here but enforced when
|
|
13
|
+
* the multi-cohort (AGG-4) and non-inclusion (AGG-5) tracks land. The two cost
|
|
14
|
+
* conditions are advertised metadata only - the protocol performs no payment or
|
|
15
|
+
* settlement (consistent with ADR 008).
|
|
16
|
+
*/
|
|
17
|
+
import { MAX_RECOVERY_SEQUENCE } from './recovery-policy.js';
|
|
18
|
+
/** Beacon types that support aggregation (singleton is single-party only, per ADR 037). */
|
|
19
|
+
export const KNOWN_BEACON_TYPES = ['CASBeacon', 'SMTBeacon'];
|
|
20
|
+
/** Funding models an operator may advertise. Only 'operator-funded' is implemented (ADR 042). */
|
|
21
|
+
export const KNOWN_FUNDING_MODELS = ['operator-funded', 'participant-funded'];
|
|
22
|
+
/** Validate an optional [min, max] integer pair. */
|
|
23
|
+
function checkPair(problems, label, min, max) {
|
|
24
|
+
if (min !== undefined && (!Number.isInteger(min) || min < 0)) {
|
|
25
|
+
problems.push(`min${label} must be an integer >= 0`);
|
|
26
|
+
}
|
|
27
|
+
if (max !== undefined && (!Number.isInteger(max) || max < 0)) {
|
|
28
|
+
problems.push(`max${label} must be an integer >= 0`);
|
|
29
|
+
}
|
|
30
|
+
if (min !== undefined && max !== undefined && Number.isInteger(min) && Number.isInteger(max) && max < min) {
|
|
31
|
+
problems.push(`max${label} must be >= min${label}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Validate an optional advertised cost. */
|
|
35
|
+
function checkCost(problems, label, cost) {
|
|
36
|
+
if (cost === undefined)
|
|
37
|
+
return;
|
|
38
|
+
if (typeof cost.amount !== 'number' || !Number.isFinite(cost.amount) || cost.amount < 0) {
|
|
39
|
+
problems.push(`${label}.amount must be a finite number >= 0`);
|
|
40
|
+
}
|
|
41
|
+
if (typeof cost.unit !== 'string' || cost.unit.length === 0) {
|
|
42
|
+
problems.push(`${label}.unit must be a non-empty string`);
|
|
43
|
+
}
|
|
44
|
+
if (cost.basis !== undefined && cost.basis !== 'per-did' && cost.basis !== 'per-participant') {
|
|
45
|
+
problems.push(`${label}.basis must be 'per-did' or 'per-participant'`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate a set of cohort conditions. Returns a list of human-readable problems
|
|
50
|
+
* (empty when valid) so the caller can decide how to surface them. Used by
|
|
51
|
+
* `createCohort()` to fail fast instead of discovering invalidity at finalize.
|
|
52
|
+
*/
|
|
53
|
+
export function validateCohortConditions(c) {
|
|
54
|
+
const problems = [];
|
|
55
|
+
if (!KNOWN_BEACON_TYPES.includes(c.beaconType)) {
|
|
56
|
+
problems.push(`beaconType must be one of ${KNOWN_BEACON_TYPES.join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
if (!Number.isInteger(c.minParticipants) || c.minParticipants < 1) {
|
|
59
|
+
problems.push('minParticipants must be an integer >= 1');
|
|
60
|
+
}
|
|
61
|
+
if (c.maxParticipants !== undefined) {
|
|
62
|
+
if (!Number.isInteger(c.maxParticipants) || c.maxParticipants < 1) {
|
|
63
|
+
problems.push('maxParticipants must be an integer >= 1');
|
|
64
|
+
}
|
|
65
|
+
else if (Number.isInteger(c.minParticipants) && c.maxParticipants < c.minParticipants) {
|
|
66
|
+
problems.push('maxParticipants must be >= minParticipants');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
checkPair(problems, 'DidsPerParticipant', c.minDidsPerParticipant, c.maxDidsPerParticipant);
|
|
70
|
+
checkPair(problems, 'SecondsBetweenAnnouncements', c.minSecondsBetweenAnnouncements, c.maxSecondsBetweenAnnouncements);
|
|
71
|
+
if (c.pendingUpdateTrigger !== undefined && (!Number.isInteger(c.pendingUpdateTrigger) || c.pendingUpdateTrigger < 1)) {
|
|
72
|
+
problems.push('pendingUpdateTrigger must be an integer >= 1');
|
|
73
|
+
}
|
|
74
|
+
// Fallback threshold (k of the k-of-n fallback leaf). Optional; when advertised
|
|
75
|
+
// it must be a positive integer and cannot exceed maxParticipants (the binding
|
|
76
|
+
// upper bound against the actual cohort size n is checked at keygen, where n is
|
|
77
|
+
// known). See ADR 042.
|
|
78
|
+
if (c.fallbackThreshold !== undefined) {
|
|
79
|
+
if (!Number.isInteger(c.fallbackThreshold) || c.fallbackThreshold < 1) {
|
|
80
|
+
problems.push('fallbackThreshold must be an integer >= 1');
|
|
81
|
+
}
|
|
82
|
+
else if (c.maxParticipants !== undefined && Number.isInteger(c.maxParticipants) && c.fallbackThreshold > c.maxParticipants) {
|
|
83
|
+
problems.push('fallbackThreshold must be <= maxParticipants');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
checkCost(problems, 'costOfEnrollment', c.costOfEnrollment);
|
|
87
|
+
checkCost(problems, 'costPerAnnouncement', c.costPerAnnouncement);
|
|
88
|
+
// Recovery params: the beacon output's CSV recovery leaf. Required so a
|
|
89
|
+
// missing signer can never permanently lock the funded UTXO (ADR 042). The
|
|
90
|
+
// key is an x-only (32-byte) Schnorr public key, carried as 64 hex chars.
|
|
91
|
+
if (typeof c.recoveryKey !== 'string' || !/^[0-9a-fA-F]{64}$/.test(c.recoveryKey)) {
|
|
92
|
+
problems.push('recoveryKey must be a 64-character hex string (x-only public key)');
|
|
93
|
+
}
|
|
94
|
+
if (!Number.isInteger(c.recoverySequence) || c.recoverySequence < 1 || c.recoverySequence > MAX_RECOVERY_SEQUENCE) {
|
|
95
|
+
problems.push(`recoverySequence must be a block-based BIP-68 relative timelock in [1, ${MAX_RECOVERY_SEQUENCE}]`);
|
|
96
|
+
}
|
|
97
|
+
if (c.fundingModel !== undefined && !KNOWN_FUNDING_MODELS.includes(c.fundingModel)) {
|
|
98
|
+
problems.push(`fundingModel must be one of ${KNOWN_FUNDING_MODELS.join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
return problems;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.js","sourceRoot":"","sources":["../../src/conditions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MethodError } from '@did-btcr2/common';
|
|
2
|
+
export class AggregationServiceError extends MethodError {
|
|
3
|
+
constructor(message, type = 'AggregationServiceError', data) {
|
|
4
|
+
super(message, type, data);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class AggregationParticipantError extends MethodError {
|
|
8
|
+
constructor(message, type = 'AggregationParticipantError', data) {
|
|
9
|
+
super(message, type, data);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class AggregationCohortError extends MethodError {
|
|
13
|
+
constructor(message, type = 'AggregationCohortError', data) {
|
|
14
|
+
super(message, type, data);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class SigningSessionError extends MethodError {
|
|
18
|
+
constructor(message, type = 'SigningSessionError', data) {
|
|
19
|
+
super(message, type, data);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,OAAO,uBAAwB,SAAQ,WAAW;IACtD,YAAY,OAAe,EAAE,OAAe,yBAAyB,EAAE,IAA0B;QAC/F,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,2BAA4B,SAAQ,WAAW;IAC1D,YAAY,OAAe,EAAE,OAAe,6BAA6B,EAAE,IAA0B;QACnG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,WAAW;IACrD,YAAY,OAAe,EAAE,OAAe,wBAAwB,EAAE,IAA0B;QAC9F,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IAClD,YAAY,OAAe,EAAE,OAAe,qBAAqB,EAAE,IAA0B;QAC3F,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF"}
|