@did-btcr2/method 0.36.0 → 0.37.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/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +1371 -260
- package/dist/browser.mjs +1371 -260
- package/dist/cjs/index.js +1464 -343
- package/dist/esm/core/aggregation/beacon-strategy.js +19 -6
- package/dist/esm/core/aggregation/beacon-strategy.js.map +1 -1
- package/dist/esm/core/aggregation/cohort.js +142 -33
- package/dist/esm/core/aggregation/cohort.js.map +1 -1
- package/dist/esm/core/aggregation/conditions.js +27 -0
- package/dist/esm/core/aggregation/conditions.js.map +1 -1
- package/dist/esm/core/aggregation/fallback-spend.js +132 -0
- package/dist/esm/core/aggregation/fallback-spend.js.map +1 -0
- package/dist/esm/core/aggregation/messages/base.js.map +1 -1
- package/dist/esm/core/aggregation/messages/bodies.js +46 -1
- package/dist/esm/core/aggregation/messages/bodies.js.map +1 -1
- package/dist/esm/core/aggregation/messages/constants.js +11 -0
- package/dist/esm/core/aggregation/messages/constants.js.map +1 -1
- package/dist/esm/core/aggregation/messages/factories.js +27 -1
- package/dist/esm/core/aggregation/messages/factories.js.map +1 -1
- package/dist/esm/core/aggregation/messages/guards.js +4 -1
- package/dist/esm/core/aggregation/messages/guards.js.map +1 -1
- package/dist/esm/core/aggregation/participant.js +218 -18
- package/dist/esm/core/aggregation/participant.js.map +1 -1
- package/dist/esm/core/aggregation/phases.js +6 -0
- package/dist/esm/core/aggregation/phases.js.map +1 -1
- package/dist/esm/core/aggregation/recovery-policy.js +156 -0
- package/dist/esm/core/aggregation/recovery-policy.js.map +1 -0
- package/dist/esm/core/aggregation/recovery-spend.js +106 -0
- package/dist/esm/core/aggregation/recovery-spend.js.map +1 -0
- package/dist/esm/core/aggregation/runner/aggregation-runner.js +11 -3
- package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js +106 -19
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +409 -195
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
- package/dist/esm/core/aggregation/service.js +221 -11
- package/dist/esm/core/aggregation/service.js.map +1 -1
- package/dist/esm/core/aggregation/signing-session.js +3 -3
- package/dist/esm/core/aggregation/signing-session.js.map +1 -1
- package/dist/esm/core/aggregation/transport/http/envelope.js +3 -3
- package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +1 -1
- package/dist/esm/core/aggregation/transport/http/nonce-cache.js +1 -1
- package/dist/esm/core/aggregation/transport/http/rate-limiter.js +1 -1
- package/dist/esm/core/aggregation/transport/http/request-auth.js +1 -1
- package/dist/esm/core/aggregation/transport/http/server.js +1 -1
- package/dist/esm/core/aggregation/transport/http/sse-stream.js +1 -1
- package/dist/esm/core/aggregation/transport/http/sse-writer.js +1 -1
- package/dist/esm/core/aggregation/transport/in-memory.js +1 -1
- package/dist/esm/core/aggregation/transport/nostr.js +6 -6
- package/dist/esm/core/beacon/beacon.js +15 -10
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/beacon/cas-beacon.js +6 -6
- package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
- package/dist/esm/core/beacon/singleton-beacon.js +2 -2
- package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
- package/dist/esm/core/beacon/smt-beacon.js +3 -3
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/resolver.js +8 -8
- package/dist/esm/core/resolver.js.map +1 -1
- package/dist/esm/core/updater.js +6 -6
- package/dist/esm/core/updater.js.map +1 -1
- package/dist/esm/did-btcr2.js +2 -2
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/types/core/aggregation/beacon-strategy.d.ts +8 -4
- package/dist/types/core/aggregation/beacon-strategy.d.ts.map +1 -1
- package/dist/types/core/aggregation/cohort.d.ts +88 -17
- package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
- package/dist/types/core/aggregation/conditions.d.ts +28 -0
- package/dist/types/core/aggregation/conditions.d.ts.map +1 -1
- package/dist/types/core/aggregation/fallback-spend.d.ts +94 -0
- package/dist/types/core/aggregation/fallback-spend.d.ts.map +1 -0
- package/dist/types/core/aggregation/logger.d.ts +1 -1
- package/dist/types/core/aggregation/messages/base.d.ts +6 -0
- package/dist/types/core/aggregation/messages/base.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/bodies.d.ts +50 -2
- package/dist/types/core/aggregation/messages/bodies.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/constants.d.ts +8 -0
- package/dist/types/core/aggregation/messages/constants.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/factories.d.ts +40 -0
- package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/guards.d.ts.map +1 -1
- package/dist/types/core/aggregation/participant.d.ts +47 -4
- package/dist/types/core/aggregation/participant.d.ts.map +1 -1
- package/dist/types/core/aggregation/phases.d.ts +8 -2
- package/dist/types/core/aggregation/phases.d.ts.map +1 -1
- package/dist/types/core/aggregation/recovery-policy.d.ts +132 -0
- package/dist/types/core/aggregation/recovery-policy.d.ts.map +1 -0
- package/dist/types/core/aggregation/recovery-spend.d.ts +94 -0
- package/dist/types/core/aggregation/recovery-spend.d.ts.map +1 -0
- package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +13 -3
- package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/events.d.ts +48 -16
- package/dist/types/core/aggregation/runner/events.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/participant-runner.d.ts +23 -14
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts +102 -23
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/service.d.ts +26 -2
- package/dist/types/core/aggregation/service.d.ts.map +1 -1
- package/dist/types/core/aggregation/signing-session.d.ts +2 -2
- package/dist/types/core/aggregation/signing-session.d.ts.map +1 -1
- package/dist/types/core/aggregation/transport/http/envelope.d.ts +3 -3
- package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +1 -1
- package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +1 -1
- package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +1 -1
- package/dist/types/core/aggregation/transport/http/request-auth.d.ts +1 -1
- package/dist/types/core/aggregation/transport/http/server.d.ts +1 -1
- package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +2 -2
- package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +1 -1
- package/dist/types/core/aggregation/transport/in-memory.d.ts +1 -1
- package/dist/types/core/aggregation/transport/nostr.d.ts +4 -4
- package/dist/types/core/aggregation/transport/transport.d.ts +4 -4
- package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +7 -7
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/cas-beacon.d.ts +2 -2
- package/dist/types/core/beacon/interfaces.d.ts +1 -1
- package/dist/types/core/resolver.d.ts +3 -3
- package/dist/types/core/updater.d.ts +4 -4
- package/dist/types/did-btcr2.d.ts +2 -2
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/aggregation/beacon-strategy.ts +27 -9
- package/src/core/aggregation/cohort.ts +171 -35
- package/src/core/aggregation/conditions.ts +56 -0
- package/src/core/aggregation/fallback-spend.ts +223 -0
- package/src/core/aggregation/logger.ts +1 -1
- package/src/core/aggregation/messages/base.ts +6 -0
- package/src/core/aggregation/messages/bodies.ts +94 -1
- package/src/core/aggregation/messages/constants.ts +12 -0
- package/src/core/aggregation/messages/factories.ts +57 -0
- package/src/core/aggregation/messages/guards.ts +6 -0
- package/src/core/aggregation/participant.ts +264 -20
- package/src/core/aggregation/phases.ts +9 -0
- package/src/core/aggregation/recovery-policy.ts +237 -0
- package/src/core/aggregation/recovery-spend.ts +192 -0
- package/src/core/aggregation/runner/aggregation-runner.ts +27 -5
- package/src/core/aggregation/runner/events.ts +47 -19
- package/src/core/aggregation/runner/participant-runner.ts +113 -31
- package/src/core/aggregation/runner/service-runner.ts +484 -203
- package/src/core/aggregation/service.ts +260 -14
- package/src/core/aggregation/signing-session.ts +3 -3
- package/src/core/aggregation/transport/http/envelope.ts +3 -3
- package/src/core/aggregation/transport/http/inbox-buffer.ts +1 -1
- package/src/core/aggregation/transport/http/nonce-cache.ts +1 -1
- package/src/core/aggregation/transport/http/rate-limiter.ts +1 -1
- package/src/core/aggregation/transport/http/request-auth.ts +1 -1
- package/src/core/aggregation/transport/http/server.ts +1 -1
- package/src/core/aggregation/transport/http/sse-stream.ts +2 -2
- package/src/core/aggregation/transport/http/sse-writer.ts +1 -1
- package/src/core/aggregation/transport/in-memory.ts +1 -1
- package/src/core/aggregation/transport/nostr.ts +6 -6
- package/src/core/aggregation/transport/transport.ts +4 -4
- package/src/core/beacon/beacon.ts +16 -11
- package/src/core/beacon/cas-beacon.ts +7 -7
- package/src/core/beacon/interfaces.ts +1 -1
- package/src/core/beacon/singleton-beacon.ts +2 -2
- package/src/core/beacon/smt-beacon.ts +3 -3
- package/src/core/resolver.ts +8 -8
- package/src/core/updater.ts +6 -6
- package/src/did-btcr2.ts +2 -2
- package/src/index.ts +3 -0
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* Guards validate both `type` and the presence of required body fields so they
|
|
11
11
|
* are safe to use on messages that have round-tripped through JSON / a relay.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { KNOWN_FUNDING_MODELS } from '../conditions.js';
|
|
14
|
+
import { MAX_RECOVERY_SEQUENCE } from '../recovery-policy.js';
|
|
15
|
+
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, FALLBACK_AUTHORIZATION_REQUEST, FALLBACK_SIGNATURE, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_NONINCLUDED, SUBMIT_UPDATE, VALIDATION_ACK, } from './constants.js';
|
|
14
16
|
// ── Type guards ───────────────────────────────────────────────────────────
|
|
15
17
|
// Each guard validates `type` plus required body fields so it's safe to use
|
|
16
18
|
// on messages that have round-tripped through JSON / a relay.
|
|
@@ -25,6 +27,16 @@ const optIntMin = (b, k, min) => {
|
|
|
25
27
|
const v = b ? b[k] : undefined;
|
|
26
28
|
return v === undefined || (typeof v === 'number' && Number.isInteger(v) && v >= min);
|
|
27
29
|
};
|
|
30
|
+
/** Present, an integer, and within [min, max] inclusive. */
|
|
31
|
+
const intInRange = (b, k, min, max) => {
|
|
32
|
+
const v = b ? b[k] : undefined;
|
|
33
|
+
return typeof v === 'number' && Number.isInteger(v) && v >= min && v <= max;
|
|
34
|
+
};
|
|
35
|
+
/** Absent, or present as a string in the allowed set. */
|
|
36
|
+
const optStrOneOf = (b, k, allowed) => {
|
|
37
|
+
const v = b ? b[k] : undefined;
|
|
38
|
+
return v === undefined || (typeof v === 'string' && allowed.includes(v));
|
|
39
|
+
};
|
|
28
40
|
const hasBool = (b, k) => !!b && typeof b[k] === 'boolean';
|
|
29
41
|
const hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
|
|
30
42
|
const hasBytesArray = (b, k) => {
|
|
@@ -42,6 +54,20 @@ export function isCohortAdvertMessage(m) {
|
|
|
42
54
|
&& optIntMin(m.body, 'maxParticipants', 1)
|
|
43
55
|
&& hasStr(m.body, 'beaconType')
|
|
44
56
|
&& hasStr(m.body, 'network')
|
|
57
|
+
// Recovery params are mandatory: a participant must see the recovery terms
|
|
58
|
+
// (key + timelock) before funding the beacon UTXO (ADR 042). recoverySequence
|
|
59
|
+
// is range-checked here because the participant funds based on the advert and
|
|
60
|
+
// does not separately run validateCohortConditions: an out-of-range value
|
|
61
|
+
// (e.g. one with the BIP-68 disable bit set) must be rejected before funding.
|
|
62
|
+
&& hasStr(m.body, 'recoveryKey')
|
|
63
|
+
&& intInRange(m.body, 'recoverySequence', 1, MAX_RECOVERY_SEQUENCE)
|
|
64
|
+
// fundingModel is optional on the wire (absent means the operator-funded
|
|
65
|
+
// default), but when present it must name a known model.
|
|
66
|
+
&& optStrOneOf(m.body, 'fundingModel', KNOWN_FUNDING_MODELS)
|
|
67
|
+
// fallbackThreshold is optional (absent means n-1 at keygen); when present it
|
|
68
|
+
// must be a positive integer. The upper bound against the cohort size is
|
|
69
|
+
// checked when the participant recomputes the beacon address.
|
|
70
|
+
&& optIntMin(m.body, 'fallbackThreshold', 1)
|
|
45
71
|
&& hasBytes(m.body, 'communicationPk');
|
|
46
72
|
}
|
|
47
73
|
export function isCohortOptInMessage(m) {
|
|
@@ -64,6 +90,9 @@ export function isSubmitUpdateMessage(m) {
|
|
|
64
90
|
&& hasStr(m.body, 'cohortId')
|
|
65
91
|
&& !!m.body && typeof m.body.signedUpdate === 'object';
|
|
66
92
|
}
|
|
93
|
+
export function isSubmitNonIncludedMessage(m) {
|
|
94
|
+
return m.type === SUBMIT_NONINCLUDED && hasStr(m.body, 'cohortId');
|
|
95
|
+
}
|
|
67
96
|
export function isDistributeAggregatedDataMessage(m) {
|
|
68
97
|
return m.type === DISTRIBUTE_AGGREGATED_DATA
|
|
69
98
|
&& hasStr(m.body, 'cohortId')
|
|
@@ -101,4 +130,20 @@ export function isSignatureAuthorizationMessage(m) {
|
|
|
101
130
|
&& hasStr(m.body, 'sessionId')
|
|
102
131
|
&& hasBytes(m.body, 'partialSignature');
|
|
103
132
|
}
|
|
133
|
+
export function isFallbackAuthorizationRequestMessage(m) {
|
|
134
|
+
return m.type === FALLBACK_AUTHORIZATION_REQUEST
|
|
135
|
+
&& hasStr(m.body, 'cohortId')
|
|
136
|
+
&& hasStr(m.body, 'sessionId')
|
|
137
|
+
&& hasStr(m.body, 'pendingTx')
|
|
138
|
+
&& hasStr(m.body, 'prevOutScriptHex')
|
|
139
|
+
&& hasStr(m.body, 'prevOutValue')
|
|
140
|
+
&& hasStr(m.body, 'fallbackLeafScriptHex');
|
|
141
|
+
}
|
|
142
|
+
export function isFallbackSignatureMessage(m) {
|
|
143
|
+
return m.type === FALLBACK_SIGNATURE
|
|
144
|
+
&& hasStr(m.body, 'cohortId')
|
|
145
|
+
&& hasStr(m.body, 'sessionId')
|
|
146
|
+
&& hasBytes(m.body, 'signerPk')
|
|
147
|
+
&& hasBytes(m.body, 'fallbackSignature');
|
|
148
|
+
}
|
|
104
149
|
//# sourceMappingURL=bodies.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bodies.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/bodies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"bodies.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/bodies.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - Step 3 (Aggregate & Validate): DISTRIBUTE_AGGREGATED_DATA, VALIDATION_ACK
|
|
8
8
|
* - Step 4 (Sign & Broadcast): AUTHORIZATION_REQUEST, NONCE_CONTRIBUTION,
|
|
9
9
|
* AGGREGATED_NONCE, SIGNATURE_AUTHORIZATION
|
|
10
|
+
* - Step 4 fallback (k-of-n script path, ADR 042): FALLBACK_AUTHORIZATION_REQUEST,
|
|
11
|
+
* FALLBACK_SIGNATURE
|
|
10
12
|
*/
|
|
11
13
|
export const AGGREGATION_MESSAGE_PREFIX = 'https://btcr2.dev/aggregation';
|
|
12
14
|
// Step 1: Cohort Formation
|
|
@@ -16,6 +18,8 @@ export const COHORT_OPT_IN_ACCEPT = `${AGGREGATION_MESSAGE_PREFIX}/keygen/cohort
|
|
|
16
18
|
export const COHORT_READY = `${AGGREGATION_MESSAGE_PREFIX}/keygen/cohort_ready`;
|
|
17
19
|
// Step 2 + 3: Update Submission, Aggregation, Validation
|
|
18
20
|
export const SUBMIT_UPDATE = `${AGGREGATION_MESSAGE_PREFIX}/update/submit_update`;
|
|
21
|
+
/** A member with no update this round declines explicitly (cooperative non-inclusion). */
|
|
22
|
+
export const SUBMIT_NONINCLUDED = `${AGGREGATION_MESSAGE_PREFIX}/update/submit_nonincluded`;
|
|
19
23
|
export const DISTRIBUTE_AGGREGATED_DATA = `${AGGREGATION_MESSAGE_PREFIX}/update/distribute_aggregated_data`;
|
|
20
24
|
export const VALIDATION_ACK = `${AGGREGATION_MESSAGE_PREFIX}/update/validation_ack`;
|
|
21
25
|
// Step 4: Signing
|
|
@@ -23,4 +27,11 @@ export const AUTHORIZATION_REQUEST = `${AGGREGATION_MESSAGE_PREFIX}/sign/authori
|
|
|
23
27
|
export const NONCE_CONTRIBUTION = `${AGGREGATION_MESSAGE_PREFIX}/sign/nonce_contribution`;
|
|
24
28
|
export const AGGREGATED_NONCE = `${AGGREGATION_MESSAGE_PREFIX}/sign/aggregated_nonce`;
|
|
25
29
|
export const SIGNATURE_AUTHORIZATION = `${AGGREGATION_MESSAGE_PREFIX}/sign/signature_authorization`;
|
|
30
|
+
// Step 4 fallback: k-of-n script-path signing when the optimistic n-of-n key
|
|
31
|
+
// path stalls (graceful liveness, ADR 042). A single standalone BIP-340
|
|
32
|
+
// signature per member, no nonce round.
|
|
33
|
+
/** Service asks members to sign the fallback (k-of-n) script path of the beacon tx. */
|
|
34
|
+
export const FALLBACK_AUTHORIZATION_REQUEST = `${AGGREGATION_MESSAGE_PREFIX}/sign/fallback_authorization_request`;
|
|
35
|
+
/** A member returns a standalone BIP-340 signature over the fallback script-path sighash. */
|
|
36
|
+
export const FALLBACK_SIGNATURE = `${AGGREGATION_MESSAGE_PREFIX}/sign/fallback_signature`;
|
|
26
37
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/constants.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,+BAA+B,CAAC;AAE1E,2BAA2B;AAC3B,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,0BAA0B,uBAAuB,CAAC;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,0BAA0B,uBAAuB,CAAC;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,0BAA0B,8BAA8B,CAAC;AAChG,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,0BAA0B,sBAAsB,CAAC;AAEhF,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,0BAA0B,uBAAuB,CAAC;AAClF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,0BAA0B,4BAA4B,CAAC;AAC5F,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,0BAA0B,oCAAoC,CAAC;AAC5G,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,0BAA0B,wBAAwB,CAAC;AAEpF,kBAAkB;AAClB,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,0BAA0B,6BAA6B,CAAC;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,0BAA0B,0BAA0B,CAAC;AAC1F,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,0BAA0B,wBAAwB,CAAC;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,0BAA0B,+BAA+B,CAAC;AAEpG,6EAA6E;AAC7E,wEAAwE;AACxE,wCAAwC;AACxC,uFAAuF;AACvF,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,0BAA0B,sCAAsC,CAAC;AAClH,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,0BAA0B,0BAA0B,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseMessage } from './base.js';
|
|
2
|
-
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_UPDATE, VALIDATION_ACK, } from './constants.js';
|
|
2
|
+
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, FALLBACK_AUTHORIZATION_REQUEST, FALLBACK_SIGNATURE, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_NONINCLUDED, SUBMIT_UPDATE, VALIDATION_ACK, } from './constants.js';
|
|
3
3
|
/**
|
|
4
4
|
* Factory function for creating a Cohort Advert message, which is used to announce the formation of
|
|
5
5
|
* a new cohort and invite participants to join.
|
|
@@ -40,6 +40,15 @@ export function createSubmitUpdateMessage(fields) {
|
|
|
40
40
|
const { from, to, ...body } = fields;
|
|
41
41
|
return new BaseMessage({ type: SUBMIT_UPDATE, from, to, body });
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Factory for a Submit Non-Included message: a member declares it has no update
|
|
45
|
+
* this round (cooperative non-inclusion). Membership is proven by the signed
|
|
46
|
+
* envelope, so the body carries only the cohortId.
|
|
47
|
+
*/
|
|
48
|
+
export function createSubmitNonIncludedMessage(fields) {
|
|
49
|
+
const { from, to, ...body } = fields;
|
|
50
|
+
return new BaseMessage({ type: SUBMIT_NONINCLUDED, from, to, body });
|
|
51
|
+
}
|
|
43
52
|
/**
|
|
44
53
|
* Factory function for creating a Distribute Aggregated Data message, which is sent by the
|
|
45
54
|
* aggregator to distribute the aggregated data to participants for validation.
|
|
@@ -110,4 +119,21 @@ export function createSignatureAuthorizationMessage(fields) {
|
|
|
110
119
|
const { from, to, ...body } = fields;
|
|
111
120
|
return new BaseMessage({ type: SIGNATURE_AUTHORIZATION, from, to, body });
|
|
112
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Factory for a Fallback Authorization Request: the service asks members to
|
|
124
|
+
* authorize the k-of-n script-path spend of the beacon transaction when the
|
|
125
|
+
* optimistic n-of-n key path stalls (ADR 042).
|
|
126
|
+
*/
|
|
127
|
+
export function createFallbackAuthorizationRequestMessage(fields) {
|
|
128
|
+
const { from, to, ...body } = fields;
|
|
129
|
+
return new BaseMessage({ type: FALLBACK_AUTHORIZATION_REQUEST, from, to, body });
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Factory for a Fallback Signature: a member's standalone BIP-340 signature over
|
|
133
|
+
* the fallback script-path sighash (no nonce round).
|
|
134
|
+
*/
|
|
135
|
+
export function createFallbackSignatureMessage(fields) {
|
|
136
|
+
const { from, to, ...body } = fields;
|
|
137
|
+
return new BaseMessage({ type: FALLBACK_SIGNATURE, from, to, body });
|
|
138
|
+
}
|
|
113
139
|
//# sourceMappingURL=factories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factories.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/factories.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"factories.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/factories.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_UPDATE, VALIDATION_ACK, } from './constants.js';
|
|
1
|
+
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, FALLBACK_AUTHORIZATION_REQUEST, FALLBACK_SIGNATURE, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_NONINCLUDED, SUBMIT_UPDATE, VALIDATION_ACK, } from './constants.js';
|
|
2
2
|
const KEYGEN_VALUES = new Set([
|
|
3
3
|
COHORT_ADVERT,
|
|
4
4
|
COHORT_OPT_IN,
|
|
@@ -7,6 +7,7 @@ const KEYGEN_VALUES = new Set([
|
|
|
7
7
|
]);
|
|
8
8
|
const UPDATE_VALUES = new Set([
|
|
9
9
|
SUBMIT_UPDATE,
|
|
10
|
+
SUBMIT_NONINCLUDED,
|
|
10
11
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
11
12
|
VALIDATION_ACK,
|
|
12
13
|
]);
|
|
@@ -15,6 +16,8 @@ const SIGN_VALUES = new Set([
|
|
|
15
16
|
NONCE_CONTRIBUTION,
|
|
16
17
|
AGGREGATED_NONCE,
|
|
17
18
|
SIGNATURE_AUTHORIZATION,
|
|
19
|
+
FALLBACK_AUTHORIZATION_REQUEST,
|
|
20
|
+
FALLBACK_SIGNATURE,
|
|
18
21
|
]);
|
|
19
22
|
/**
|
|
20
23
|
* Checks if the provided type is a valid aggregation message type.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/guards.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,0BAA0B,EAC1B,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,EACb,cAAc,GACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,aAAa,GAAgB,IAAI,GAAG,CAAC;IACzC,aAAa;IACb,aAAa;IACb,oBAAoB;IACpB,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,aAAa,GAAgB,IAAI,GAAG,CAAC;IACzC,aAAa;IACb,0BAA0B;IAC1B,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,WAAW,GAAgB,IAAI,GAAG,CAAC;IACvC,qBAAqB;IACrB,kBAAkB;IAClB,gBAAgB;IAChB,uBAAuB;
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../../../src/core/aggregation/messages/guards.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,0BAA0B,EAC1B,8BAA8B,EAC9B,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,cAAc,GACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,aAAa,GAAgB,IAAI,GAAG,CAAC;IACzC,aAAa;IACb,aAAa;IACb,oBAAoB;IACpB,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,aAAa,GAAgB,IAAI,GAAG,CAAC;IACzC,aAAa;IACb,kBAAkB;IAClB,0BAA0B;IAC1B,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,WAAW,GAAgB,IAAI,GAAG,CAAC;IACvC,qBAAqB;IACrB,kBAAkB;IAClB,gBAAgB;IAChB,uBAAuB;IACvB,8BAA8B;IAC9B,kBAAkB;CACnB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrF,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -1,15 +1,54 @@
|
|
|
1
1
|
import { canonicalHash } from '@did-btcr2/common';
|
|
2
|
+
import { schnorr } from '@noble/curves/secp256k1.js';
|
|
2
3
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
3
|
-
import { Transaction } from '@scure/btc-signer';
|
|
4
|
+
import { Script, Transaction } from '@scure/btc-signer';
|
|
4
5
|
import { getBeaconStrategy } from './beacon-strategy.js';
|
|
5
6
|
import { AggregationCohort } from './cohort.js';
|
|
6
7
|
import { AggregationParticipantError } from './errors.js';
|
|
8
|
+
import { fallbackSighash } from './fallback-spend.js';
|
|
9
|
+
import { buildFallbackLeaf } from './recovery-policy.js';
|
|
7
10
|
import { isCohortAdvertMessage } from './messages/bodies.js';
|
|
8
11
|
import { AGGREGATION_WIRE_VERSION } from './messages/base.js';
|
|
9
|
-
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, } from './messages/constants.js';
|
|
10
|
-
import { createCohortOptInMessage, createNonceContributionMessage, createSignatureAuthorizationMessage, createSubmitUpdateMessage, createValidationAckMessage, } from './messages/factories.js';
|
|
12
|
+
import { AGGREGATED_NONCE, AUTHORIZATION_REQUEST, COHORT_ADVERT, COHORT_OPT_IN_ACCEPT, COHORT_READY, DISTRIBUTE_AGGREGATED_DATA, FALLBACK_AUTHORIZATION_REQUEST, } from './messages/constants.js';
|
|
13
|
+
import { createCohortOptInMessage, createFallbackSignatureMessage, createNonceContributionMessage, createSignatureAuthorizationMessage, createSubmitNonIncludedMessage, createSubmitUpdateMessage, createValidationAckMessage, } from './messages/factories.js';
|
|
11
14
|
import { ParticipantCohortPhase } from './phases.js';
|
|
12
15
|
import { BeaconSigningSession } from './signing-session.js';
|
|
16
|
+
/**
|
|
17
|
+
* True if `tx` has an OP_RETURN output whose payload equals the 32-byte signal
|
|
18
|
+
* `signalHex`. A member binds its fallback signature to the exact signal it
|
|
19
|
+
* validated, so a coordinator that drives the fallback output selection cannot
|
|
20
|
+
* anchor a different announcement (a stale signal, or one whose CAS/SMT root
|
|
21
|
+
* omits the member's update) under the member's signature.
|
|
22
|
+
*/
|
|
23
|
+
function txEmbedsSignal(tx, signalHex) {
|
|
24
|
+
let expected;
|
|
25
|
+
try {
|
|
26
|
+
expected = hexToBytes(signalHex);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (expected.length === 0)
|
|
32
|
+
return false;
|
|
33
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
34
|
+
const script = tx.getOutput(i)?.script;
|
|
35
|
+
if (!script)
|
|
36
|
+
continue;
|
|
37
|
+
let decoded;
|
|
38
|
+
try {
|
|
39
|
+
decoded = Script.decode(script);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (decoded.length === 2 && decoded[0] === 'RETURN' && decoded[1] instanceof Uint8Array) {
|
|
45
|
+
const payload = decoded[1];
|
|
46
|
+
if (payload.length === expected.length && payload.every((b, j) => b === expected[j]))
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
13
52
|
/**
|
|
14
53
|
* Sans-I/O state machine for an Aggregation Participant.
|
|
15
54
|
*
|
|
@@ -36,7 +75,7 @@ export class AggregationParticipant {
|
|
|
36
75
|
}
|
|
37
76
|
/**
|
|
38
77
|
* Process an incoming message. Updates internal state but never produces
|
|
39
|
-
* outgoing messages
|
|
78
|
+
* outgoing messages: those come exclusively from action methods.
|
|
40
79
|
*/
|
|
41
80
|
receive(message) {
|
|
42
81
|
// Reject messages whose wire version doesn't match what this build speaks.
|
|
@@ -63,8 +102,11 @@ export class AggregationParticipant {
|
|
|
63
102
|
case AGGREGATED_NONCE:
|
|
64
103
|
this.#handleAggregatedNonce(message);
|
|
65
104
|
break;
|
|
105
|
+
case FALLBACK_AUTHORIZATION_REQUEST:
|
|
106
|
+
this.#handleFallbackAuthorizationRequest(message);
|
|
107
|
+
break;
|
|
66
108
|
default:
|
|
67
|
-
// Unknown message type
|
|
109
|
+
// Unknown message type, silently ignore
|
|
68
110
|
break;
|
|
69
111
|
}
|
|
70
112
|
}
|
|
@@ -109,13 +151,20 @@ export class AggregationParticipant {
|
|
|
109
151
|
if (!state || state.phase !== ParticipantCohortPhase.Discovered) {
|
|
110
152
|
throw new AggregationParticipantError(`Cannot join cohort ${cohortId}: not in Discovered phase.`, 'INVALID_PHASE', { cohortId, phase: state?.phase });
|
|
111
153
|
}
|
|
112
|
-
// Create local cohort to track our view
|
|
154
|
+
// Create local cohort to track our view. Carry the advertised recovery
|
|
155
|
+
// params so validateMembership recomputes the same script-tree beacon
|
|
156
|
+
// address the service derived (ADR 042); a mismatch rejects the cohort.
|
|
157
|
+
const advert = state.advert;
|
|
113
158
|
const cohort = new AggregationCohort({
|
|
114
159
|
id: cohortId,
|
|
115
160
|
serviceDid: state.serviceDid,
|
|
116
|
-
minParticipants:
|
|
117
|
-
network:
|
|
118
|
-
beaconType:
|
|
161
|
+
minParticipants: advert.minParticipants,
|
|
162
|
+
network: advert.network,
|
|
163
|
+
beaconType: advert.beaconType,
|
|
164
|
+
recoveryKey: advert.recoveryKey ? hexToBytes(advert.recoveryKey) : undefined,
|
|
165
|
+
recoverySequence: advert.recoverySequence,
|
|
166
|
+
fundingModel: advert.fundingModel,
|
|
167
|
+
fallbackThreshold: advert.fallbackThreshold,
|
|
119
168
|
});
|
|
120
169
|
state.cohort = cohort;
|
|
121
170
|
state.phase = ParticipantCohortPhase.OptedIn;
|
|
@@ -129,10 +178,10 @@ export class AggregationParticipant {
|
|
|
129
178
|
return [optInMessage];
|
|
130
179
|
}
|
|
131
180
|
#handleOptInAccept(message) {
|
|
132
|
-
// Acknowledgment from service
|
|
181
|
+
// Acknowledgment from service, no state change needed
|
|
133
182
|
void message;
|
|
134
183
|
}
|
|
135
|
-
/** Cohorts that have been finalized
|
|
184
|
+
/** Cohorts that have been finalized: beacon address available. */
|
|
136
185
|
get joinedCohorts() {
|
|
137
186
|
const map = new Map();
|
|
138
187
|
for (const [id, state] of this.#cohortStates) {
|
|
@@ -175,6 +224,7 @@ export class AggregationParticipant {
|
|
|
175
224
|
throw new AggregationParticipantError(`Cannot submit update to cohort ${cohortId}: not in CohortReady phase.`, 'INVALID_PHASE', { cohortId, phase: state?.phase });
|
|
176
225
|
}
|
|
177
226
|
state.submittedUpdate = signedUpdate;
|
|
227
|
+
state.included = true;
|
|
178
228
|
state.phase = ParticipantCohortPhase.UpdateSubmitted;
|
|
179
229
|
const message = createSubmitUpdateMessage({
|
|
180
230
|
from: this.did,
|
|
@@ -184,6 +234,28 @@ export class AggregationParticipant {
|
|
|
184
234
|
});
|
|
185
235
|
return [message];
|
|
186
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* User action: decline to submit an update this round (cooperative
|
|
239
|
+
* non-inclusion). The member stays in the cohort and still signs; it will be
|
|
240
|
+
* absent from the CAS Announcement Map, or carry a non-inclusion leaf in the
|
|
241
|
+
* SMT. Returns the SUBMIT_NONINCLUDED message to send.
|
|
242
|
+
*/
|
|
243
|
+
declineUpdate(cohortId) {
|
|
244
|
+
const state = this.#cohortStates.get(cohortId);
|
|
245
|
+
if (!state || state.phase !== ParticipantCohortPhase.CohortReady) {
|
|
246
|
+
throw new AggregationParticipantError(`Cannot decline in cohort ${cohortId}: not in CohortReady phase.`, 'INVALID_PHASE', { cohortId, phase: state?.phase });
|
|
247
|
+
}
|
|
248
|
+
// Leave submittedUpdate unset; included=false is the load-bearing signal that
|
|
249
|
+
// the validation handler uses to validate the non-inclusion slot.
|
|
250
|
+
state.included = false;
|
|
251
|
+
state.phase = ParticipantCohortPhase.NonIncluded;
|
|
252
|
+
const message = createSubmitNonIncludedMessage({
|
|
253
|
+
from: this.did,
|
|
254
|
+
to: state.serviceDid,
|
|
255
|
+
cohortId,
|
|
256
|
+
});
|
|
257
|
+
return [message];
|
|
258
|
+
}
|
|
187
259
|
/** Aggregated data awaiting user validation. */
|
|
188
260
|
get pendingValidations() {
|
|
189
261
|
const map = new Map();
|
|
@@ -194,14 +266,29 @@ export class AggregationParticipant {
|
|
|
194
266
|
}
|
|
195
267
|
return map;
|
|
196
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* The validated aggregated data retained for a cohort, regardless of phase.
|
|
271
|
+
* Unlike {@link pendingValidations} (which lists only cohorts still awaiting
|
|
272
|
+
* the validate decision), this returns the stored validation, including the
|
|
273
|
+
* participant's sidecar (the CAS Announcement map or its SMT inclusion proof),
|
|
274
|
+
* so it is still readable once the cohort reaches Complete. Returns
|
|
275
|
+
* undefined before aggregated data has been received.
|
|
276
|
+
*/
|
|
277
|
+
getValidation(cohortId) {
|
|
278
|
+
return this.#cohortStates.get(cohortId)?.validation;
|
|
279
|
+
}
|
|
197
280
|
#handleDistributeAggregatedData(message) {
|
|
198
281
|
const cohortId = message.body?.cohortId;
|
|
199
282
|
if (!cohortId)
|
|
200
283
|
return;
|
|
201
284
|
const state = this.#cohortStates.get(cohortId);
|
|
202
|
-
|
|
285
|
+
// A submitter is in UpdateSubmitted; a decliner (cooperative non-inclusion)
|
|
286
|
+
// is in NonIncluded. Both validate their own slot in the distributed data.
|
|
287
|
+
if (!state || (state.phase !== ParticipantCohortPhase.UpdateSubmitted && state.phase !== ParticipantCohortPhase.NonIncluded))
|
|
203
288
|
return;
|
|
204
|
-
|
|
289
|
+
const declined = state.included === false;
|
|
290
|
+
// A submitter must have its update stored; a decliner has none by design.
|
|
291
|
+
if (!declined && !state.submittedUpdate)
|
|
205
292
|
return;
|
|
206
293
|
const beaconType = message.body?.beaconType;
|
|
207
294
|
if (!beaconType)
|
|
@@ -210,11 +297,14 @@ export class AggregationParticipant {
|
|
|
210
297
|
if (!strategy)
|
|
211
298
|
return;
|
|
212
299
|
const signalBytesHex = message.body?.signalBytesHex ?? '';
|
|
213
|
-
|
|
300
|
+
// Decliner validates its non-inclusion slot (CAS absence / SMT non-inclusion
|
|
301
|
+
// proof); submitter validates inclusion against its update hash.
|
|
302
|
+
const expectedHash = declined ? '' : canonicalHash(state.submittedUpdate);
|
|
214
303
|
const result = strategy.validateParticipantView({
|
|
215
304
|
participantDid: this.did,
|
|
216
|
-
|
|
217
|
-
|
|
305
|
+
included: !declined,
|
|
306
|
+
submittedUpdate: declined ? undefined : state.submittedUpdate,
|
|
307
|
+
expectedHash: declined ? undefined : expectedHash,
|
|
218
308
|
body: message.body,
|
|
219
309
|
});
|
|
220
310
|
state.validation = {
|
|
@@ -225,6 +315,7 @@ export class AggregationParticipant {
|
|
|
225
315
|
matches: result.matches,
|
|
226
316
|
casAnnouncement: result.casAnnouncement,
|
|
227
317
|
smtProof: result.smtProof,
|
|
318
|
+
included: !declined,
|
|
228
319
|
};
|
|
229
320
|
state.phase = ParticipantCohortPhase.AwaitingValidation;
|
|
230
321
|
}
|
|
@@ -294,6 +385,23 @@ export class AggregationParticipant {
|
|
|
294
385
|
};
|
|
295
386
|
state.phase = ParticipantCohortPhase.AwaitingSigning;
|
|
296
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Bind a signing approval to the announcement the member validated: a beacon
|
|
390
|
+
* transaction MUST carry an OP_RETURN with the exact 32-byte signal stored when
|
|
391
|
+
* the aggregated data was distributed. Both the optimistic nonce approval and
|
|
392
|
+
* the fallback approval sign with SIGHASH_DEFAULT (committing to every output)
|
|
393
|
+
* while the coordinator drives output selection, so without this check a
|
|
394
|
+
* coordinator could anchor a different signal under the member's signature.
|
|
395
|
+
*/
|
|
396
|
+
#assertTxAnchorsValidatedSignal(cohortId, state, tx) {
|
|
397
|
+
const signalHex = state.validation?.signalBytesHex;
|
|
398
|
+
if (!signalHex) {
|
|
399
|
+
throw new AggregationParticipantError(`Cohort ${cohortId} has no validated signal to bind the signature to.`, 'MISSING_STATE', { cohortId });
|
|
400
|
+
}
|
|
401
|
+
if (!txEmbedsSignal(tx, signalHex)) {
|
|
402
|
+
throw new AggregationParticipantError(`Transaction for cohort ${cohortId} does not anchor the validated signal.`, 'SIGNAL_MISMATCH', { cohortId });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
297
405
|
/**
|
|
298
406
|
* User action: approve signing and generate nonce contribution.
|
|
299
407
|
*/
|
|
@@ -305,7 +413,12 @@ export class AggregationParticipant {
|
|
|
305
413
|
if (!state.signingRequest || !state.cohort) {
|
|
306
414
|
throw new AggregationParticipantError(`Cohort ${cohortId} missing signing request or cohort state.`, 'MISSING_STATE', { cohortId });
|
|
307
415
|
}
|
|
308
|
-
|
|
416
|
+
// allowUnknownOutputs: a beacon transaction carries an OP_RETURN signal
|
|
417
|
+
// output, which scure does not classify as a known (spendable) output type;
|
|
418
|
+
// re-parsing the raw tx would otherwise throw.
|
|
419
|
+
const tx = Transaction.fromRaw(hexToBytes(state.signingRequest.pendingTxHex), { allowUnknownOutputs: true });
|
|
420
|
+
// Refuse to sign unless the tx anchors the signal this member validated.
|
|
421
|
+
this.#assertTxAnchorsValidatedSignal(cohortId, state, tx);
|
|
309
422
|
// Derive UTXO metadata for Taproot sighash (BIP-341). Use the script
|
|
310
423
|
// supplied by the service in AUTHORIZATION_REQUEST rather than reading
|
|
311
424
|
// the change output: input and change may use different scripts in future
|
|
@@ -348,7 +461,7 @@ export class AggregationParticipant {
|
|
|
348
461
|
}
|
|
349
462
|
/**
|
|
350
463
|
* User action: generate and return the partial signature.
|
|
351
|
-
* In most UIs this is automatic after AwaitingPartialSig
|
|
464
|
+
* In most UIs this is automatic after AwaitingPartialSig, but exposing it
|
|
352
465
|
* as an explicit action lets the client UI confirm before signing if desired.
|
|
353
466
|
*/
|
|
354
467
|
generatePartialSignature(cohortId) {
|
|
@@ -370,6 +483,93 @@ export class AggregationParticipant {
|
|
|
370
483
|
partialSignature: partialSig,
|
|
371
484
|
})];
|
|
372
485
|
}
|
|
486
|
+
/** Fallback signing requests awaiting user approval (UI: review the fallback spend). */
|
|
487
|
+
get pendingFallbackRequests() {
|
|
488
|
+
const map = new Map();
|
|
489
|
+
for (const [id, state] of this.#cohortStates) {
|
|
490
|
+
if (state.phase === ParticipantCohortPhase.AwaitingFallbackSig && state.fallbackRequest) {
|
|
491
|
+
map.set(id, state.fallbackRequest);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return map;
|
|
495
|
+
}
|
|
496
|
+
#handleFallbackAuthorizationRequest(message) {
|
|
497
|
+
const cohortId = message.body?.cohortId;
|
|
498
|
+
if (!cohortId)
|
|
499
|
+
return;
|
|
500
|
+
const state = this.#cohortStates.get(cohortId);
|
|
501
|
+
if (!state || !state.cohort)
|
|
502
|
+
return;
|
|
503
|
+
// The service can fall back at any point after the member validated. This
|
|
504
|
+
// includes the local Complete phase a member reaches the moment it sends its
|
|
505
|
+
// optimistic partial signature: the cohort has NOT finalized (the service
|
|
506
|
+
// only falls back before optimistic completion), and those members are
|
|
507
|
+
// exactly the k signers the fallback needs. Signing both the optimistic
|
|
508
|
+
// partial sig and the fallback sig is safe - both authorize the same outputs,
|
|
509
|
+
// and only one witness can ever confirm the single UTXO. A genuinely failed
|
|
510
|
+
// member is excluded. Ignore a duplicate request already being processed.
|
|
511
|
+
const acceptFrom = [
|
|
512
|
+
ParticipantCohortPhase.ValidationSent,
|
|
513
|
+
ParticipantCohortPhase.AwaitingSigning,
|
|
514
|
+
ParticipantCohortPhase.NonceSent,
|
|
515
|
+
ParticipantCohortPhase.AwaitingPartialSig,
|
|
516
|
+
ParticipantCohortPhase.Complete,
|
|
517
|
+
];
|
|
518
|
+
if (!acceptFrom.includes(state.phase))
|
|
519
|
+
return;
|
|
520
|
+
const sessionId = message.body?.sessionId;
|
|
521
|
+
const pendingTxHex = message.body?.pendingTx;
|
|
522
|
+
const prevOutScriptHex = message.body?.prevOutScriptHex;
|
|
523
|
+
const prevOutValue = message.body?.prevOutValue;
|
|
524
|
+
const fallbackLeafScriptHex = message.body?.fallbackLeafScriptHex;
|
|
525
|
+
if (!sessionId || !pendingTxHex || !prevOutScriptHex || !prevOutValue || !fallbackLeafScriptHex)
|
|
526
|
+
return;
|
|
527
|
+
state.fallbackRequest = { cohortId, sessionId, pendingTxHex, prevOutScriptHex, prevOutValue, fallbackLeafScriptHex };
|
|
528
|
+
// The optimistic path is abandoned; wipe any retained secret nonce for it.
|
|
529
|
+
state.signingSession?.clearSecrets();
|
|
530
|
+
state.phase = ParticipantCohortPhase.AwaitingFallbackSig;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* User action: authorize the fallback spend. Recomputes the k-of-n fallback
|
|
534
|
+
* leaf from the member's OWN cohort state (not the service-provided script),
|
|
535
|
+
* computes the BIP-341 script-path sighash over the requested transaction, and
|
|
536
|
+
* returns a standalone BIP-340 signature (no nonce round). The member completes
|
|
537
|
+
* once it has contributed; the service needs only k of these.
|
|
538
|
+
*/
|
|
539
|
+
approveFallback(cohortId) {
|
|
540
|
+
const state = this.#cohortStates.get(cohortId);
|
|
541
|
+
if (!state || state.phase !== ParticipantCohortPhase.AwaitingFallbackSig) {
|
|
542
|
+
throw new AggregationParticipantError(`Cannot approve fallback for cohort ${cohortId}: not in AwaitingFallbackSig phase.`, 'INVALID_PHASE', { cohortId, phase: state?.phase });
|
|
543
|
+
}
|
|
544
|
+
if (!state.fallbackRequest || !state.cohort) {
|
|
545
|
+
throw new AggregationParticipantError(`Cohort ${cohortId} missing fallback request or cohort state.`, 'MISSING_STATE', { cohortId });
|
|
546
|
+
}
|
|
547
|
+
const req = state.fallbackRequest;
|
|
548
|
+
const tx = Transaction.fromRaw(hexToBytes(req.pendingTxHex), { allowUnknownOutputs: true });
|
|
549
|
+
const prevOutScript = hexToBytes(req.prevOutScriptHex);
|
|
550
|
+
const prevOutValue = BigInt(req.prevOutValue);
|
|
551
|
+
// Refuse to sign unless the fallback tx anchors the signal this member
|
|
552
|
+
// validated (the coordinator drives output selection on the fallback path).
|
|
553
|
+
this.#assertTxAnchorsValidatedSignal(cohortId, state, tx);
|
|
554
|
+
// Recompute the fallback leaf from our own cohort keys so a malicious service
|
|
555
|
+
// cannot induce a signature over a different leaf than the one the funded
|
|
556
|
+
// address commits to.
|
|
557
|
+
const fallbackLeaf = buildFallbackLeaf({
|
|
558
|
+
cohortKeys: state.cohort.cohortKeys,
|
|
559
|
+
fallbackThreshold: state.cohort.effectiveFallbackThreshold,
|
|
560
|
+
});
|
|
561
|
+
const sighash = fallbackSighash(tx, 0, prevOutScript, prevOutValue, fallbackLeaf);
|
|
562
|
+
const signature = this.#signer.withSecret(secretKey => schnorr.sign(sighash, secretKey));
|
|
563
|
+
state.phase = ParticipantCohortPhase.Complete;
|
|
564
|
+
return [createFallbackSignatureMessage({
|
|
565
|
+
from: this.did,
|
|
566
|
+
to: state.serviceDid,
|
|
567
|
+
cohortId,
|
|
568
|
+
sessionId: req.sessionId,
|
|
569
|
+
signerPk: this.publicKey.slice(1),
|
|
570
|
+
fallbackSignature: signature,
|
|
571
|
+
})];
|
|
572
|
+
}
|
|
373
573
|
getCohortPhase(cohortId) {
|
|
374
574
|
return this.#cohortStates.get(cohortId)?.phase;
|
|
375
575
|
}
|