@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
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback spend builder for aggregate beacon outputs.
|
|
3
|
+
*
|
|
4
|
+
* An aggregate beacon UTXO is a Taproot output whose key path is the cohort's
|
|
5
|
+
* n-of-n MuSig2 aggregate key and whose script path carries a k-of-n fallback
|
|
6
|
+
* leaf (`p2tr_ms` CHECKSIGADD) plus a CSV recovery leaf (see
|
|
7
|
+
* {@link ./recovery-policy.ts} and ADR 042). When the optimistic n-of-n key path
|
|
8
|
+
* stalls (a missing or defecting signer), the cohort can still push the SAME
|
|
9
|
+
* announcement transaction through the fallback leaf with any k members'
|
|
10
|
+
* signatures, instead of abandoning the round or waiting out the recovery
|
|
11
|
+
* timelock.
|
|
12
|
+
*
|
|
13
|
+
* Unlike the MuSig2 key path, the fallback is a plain k-of-n script-path spend:
|
|
14
|
+
* each signer produces a standalone BIP-340 signature over the script-path
|
|
15
|
+
* sighash (no nonce round), and the coordinator assembles k of them into the
|
|
16
|
+
* witness. The coordinator never holds a participant secret - it injects the
|
|
17
|
+
* collected signatures and finalizes.
|
|
18
|
+
*
|
|
19
|
+
* This module is pure with respect to the network: it takes the beacon
|
|
20
|
+
* transaction, the cohort and recovery parameters, the spent output, and the
|
|
21
|
+
* collected signatures, and returns a finalized {@link Transaction}.
|
|
22
|
+
* Broadcasting is the caller's concern.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { getNetwork } from '@did-btcr2/bitcoin';
|
|
26
|
+
import { schnorr } from '@noble/curves/secp256k1.js';
|
|
27
|
+
import type { Transaction} from '@scure/btc-signer';
|
|
28
|
+
import { SigHash, p2tr } from '@scure/btc-signer';
|
|
29
|
+
import { keyAggExport, keyAggregate, sortKeys } from '@scure/btc-signer/musig2';
|
|
30
|
+
import { AggregationCohortError } from './errors.js';
|
|
31
|
+
import type { FundingModel } from './recovery-policy.js';
|
|
32
|
+
import {
|
|
33
|
+
DEFAULT_FUNDING_MODEL,
|
|
34
|
+
TAPROOT_LEAF_VERSION,
|
|
35
|
+
buildFallbackLeaf,
|
|
36
|
+
buildRecoveryLeaves,
|
|
37
|
+
resolveFallbackThreshold,
|
|
38
|
+
tapLeafHash,
|
|
39
|
+
} from './recovery-policy.js';
|
|
40
|
+
|
|
41
|
+
/** A single member's standalone signature over the fallback script-path sighash. */
|
|
42
|
+
export interface FallbackSignature {
|
|
43
|
+
/** Signer's x-only public key (32 bytes). Must be one of the cohort's keys. */
|
|
44
|
+
pubKey: Uint8Array;
|
|
45
|
+
/** 64-byte BIP-340 Schnorr signature over the script-path sighash. */
|
|
46
|
+
signature: Uint8Array;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Inputs to {@link buildFallbackSpend}. */
|
|
50
|
+
export interface FallbackSpendParams {
|
|
51
|
+
/**
|
|
52
|
+
* The beacon announcement transaction to finalize. Input {@link inputIndex}
|
|
53
|
+
* spends the beacon UTXO; its key-path witness must not be set (the fallback
|
|
54
|
+
* assembles a script-path witness instead).
|
|
55
|
+
*/
|
|
56
|
+
pendingTx: Transaction;
|
|
57
|
+
/** Index of the beacon input within {@link pendingTx}. Defaults to 0. */
|
|
58
|
+
inputIndex?: number;
|
|
59
|
+
/**
|
|
60
|
+
* The cohort's participant public keys (compressed secp256k1, 33 bytes).
|
|
61
|
+
* Sorted internally per BIP-327 so the reconstructed tree matches the funded
|
|
62
|
+
* output.
|
|
63
|
+
*/
|
|
64
|
+
cohortKeys: Uint8Array[];
|
|
65
|
+
/**
|
|
66
|
+
* The advertised k of the k-of-n fallback leaf, or omit for the cohort's n-1
|
|
67
|
+
* default. Resolved against `cohortKeys.length` exactly as the cohort does.
|
|
68
|
+
*/
|
|
69
|
+
fallbackThreshold?: number;
|
|
70
|
+
/** Operator recovery key, x-only (32 bytes). Needed to reconstruct the script tree. */
|
|
71
|
+
recoveryKey: Uint8Array;
|
|
72
|
+
/** Relative-timelock (BIP-68) the recovery leaf enforces. Needed to reconstruct the script tree. */
|
|
73
|
+
recoverySequence: number;
|
|
74
|
+
/** Funding model governing the leaves. Defaults to 'operator-funded'. */
|
|
75
|
+
fundingModel?: FundingModel;
|
|
76
|
+
/** Bitcoin network name (bitcoin/mainnet, mutinynet, signet, testnet, regtest). */
|
|
77
|
+
network: string;
|
|
78
|
+
/** scriptPubKey of the beacon UTXO being spent. Must equal the reconstructed tree output script. */
|
|
79
|
+
prevOutScript: Uint8Array;
|
|
80
|
+
/** Value of the beacon UTXO in satoshis. */
|
|
81
|
+
prevOutValue: bigint;
|
|
82
|
+
/**
|
|
83
|
+
* Standalone signatures collected from cohort members. At least
|
|
84
|
+
* {@link fallbackThreshold} valid, distinct-by-key signatures are required;
|
|
85
|
+
* exactly k are injected (a k-of-n CHECKSIGADD leaf demands exactly k).
|
|
86
|
+
*/
|
|
87
|
+
signatures: FallbackSignature[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
91
|
+
return a.length === b.length && a.every((x, i) => x === b[i]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Compute the BIP-341 script-path sighash a cohort member must sign to authorize
|
|
96
|
+
* the fallback spend. The leaf is the k-of-n fallback leaf; signers produce a
|
|
97
|
+
* standalone BIP-340 signature over this digest with SIGHASH_DEFAULT.
|
|
98
|
+
*/
|
|
99
|
+
export function fallbackSighash(
|
|
100
|
+
tx: Transaction,
|
|
101
|
+
inputIndex: number,
|
|
102
|
+
prevOutScript: Uint8Array,
|
|
103
|
+
prevOutValue: bigint,
|
|
104
|
+
fallbackLeafScript: Uint8Array,
|
|
105
|
+
): Uint8Array {
|
|
106
|
+
return tx.preimageWitnessV1(
|
|
107
|
+
inputIndex,
|
|
108
|
+
[ prevOutScript ],
|
|
109
|
+
SigHash.DEFAULT,
|
|
110
|
+
[ prevOutValue ],
|
|
111
|
+
undefined,
|
|
112
|
+
fallbackLeafScript,
|
|
113
|
+
TAPROOT_LEAF_VERSION,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Assemble and finalize a k-of-n fallback spend of an aggregate beacon UTXO.
|
|
119
|
+
*
|
|
120
|
+
* Reconstructs the funded Taproot output (MuSig2 internal key + fallback/recovery
|
|
121
|
+
* script tree), verifies each collected signature against the script-path
|
|
122
|
+
* sighash and a cohort key, injects exactly k distinct valid signatures into the
|
|
123
|
+
* fallback leaf, and finalizes the witness. The returned transaction is the same
|
|
124
|
+
* announcement the optimistic path would have produced, signed via the script
|
|
125
|
+
* path instead of the key path.
|
|
126
|
+
*
|
|
127
|
+
* @throws {AggregationCohortError} when there are no cohort keys, the spent
|
|
128
|
+
* script does not match the reconstructed tree output, or fewer than k valid
|
|
129
|
+
* distinct signatures were supplied.
|
|
130
|
+
*/
|
|
131
|
+
export function buildFallbackSpend(params: FallbackSpendParams): Transaction {
|
|
132
|
+
const {
|
|
133
|
+
pendingTx, cohortKeys, recoveryKey, recoverySequence,
|
|
134
|
+
fundingModel, network, prevOutScript, prevOutValue, signatures,
|
|
135
|
+
} = params;
|
|
136
|
+
const inputIndex = params.inputIndex ?? 0;
|
|
137
|
+
|
|
138
|
+
if(cohortKeys.length === 0) {
|
|
139
|
+
throw new AggregationCohortError(
|
|
140
|
+
'Cannot build fallback spend: no cohort keys.',
|
|
141
|
+
'NO_COHORT_KEYS'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Resolve k the same way the cohort did, so the reconstructed leaf matches.
|
|
146
|
+
const fallbackThreshold = resolveFallbackThreshold(params.fallbackThreshold, cohortKeys.length);
|
|
147
|
+
|
|
148
|
+
// Reconstruct the funded output the same way the cohort derived its address.
|
|
149
|
+
const internalKey = keyAggExport(keyAggregate(sortKeys(cohortKeys)));
|
|
150
|
+
const leaves = buildRecoveryLeaves(fundingModel ?? DEFAULT_FUNDING_MODEL, {
|
|
151
|
+
recoveryKey, recoverySequence, cohortKeys, fallbackThreshold,
|
|
152
|
+
});
|
|
153
|
+
const net = getNetwork(network);
|
|
154
|
+
// allowUnknownOutputs: the CSV recovery leaf is a custom script (see cohort.ts).
|
|
155
|
+
const payment = p2tr(internalKey, leaves, net, true);
|
|
156
|
+
|
|
157
|
+
// The output we are spending must be the cohort's funded beacon output; if the
|
|
158
|
+
// reconstructed tree script differs, the witness would not match the chain.
|
|
159
|
+
if(!bytesEqual(payment.script, prevOutScript)) {
|
|
160
|
+
throw new AggregationCohortError(
|
|
161
|
+
'Reconstructed beacon output script does not match the spent prevout script.',
|
|
162
|
+
'PREVOUT_SCRIPT_MISMATCH'
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const fallbackLeaf = buildFallbackLeaf({ cohortKeys, fallbackThreshold });
|
|
167
|
+
// Locate the fallback leaf's tapLeafScript entry: each entry is
|
|
168
|
+
// [controlBlock, script || leafVersion]; strip the trailing version byte to
|
|
169
|
+
// compare the script.
|
|
170
|
+
const leafEntry = payment.tapLeafScript?.find(([ , scriptVer ]) => {
|
|
171
|
+
const script = scriptVer.slice(0, scriptVer.length - 1);
|
|
172
|
+
return bytesEqual(script, fallbackLeaf);
|
|
173
|
+
});
|
|
174
|
+
if(!leafEntry) {
|
|
175
|
+
throw new AggregationCohortError(
|
|
176
|
+
'Could not locate the fallback leaf in the reconstructed beacon output.',
|
|
177
|
+
'FALLBACK_LEAF_NOT_FOUND'
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Validate the collected signatures against the script-path sighash. The leaf
|
|
182
|
+
// is keyed by x-only cohort keys, so a valid signature must verify against one
|
|
183
|
+
// of them. Keep only distinct, valid signatures (first occurrence per key).
|
|
184
|
+
const cohortXOnly = sortKeys(cohortKeys).map(k => k.slice(1));
|
|
185
|
+
const tx = pendingTx;
|
|
186
|
+
const sighash = fallbackSighash(tx, inputIndex, prevOutScript, prevOutValue, fallbackLeaf);
|
|
187
|
+
|
|
188
|
+
const leafHash = tapLeafHash(fallbackLeaf);
|
|
189
|
+
const accepted = new Map<string, Uint8Array>(); // x-only hex to signature
|
|
190
|
+
for(const { pubKey, signature } of signatures) {
|
|
191
|
+
if(pubKey.length !== 32 || signature.length !== 64) continue;
|
|
192
|
+
const isCohortKey = cohortXOnly.some(k => bytesEqual(k, pubKey));
|
|
193
|
+
if(!isCohortKey) continue;
|
|
194
|
+
const hex = Array.from(pubKey, b => b.toString(16).padStart(2, '0')).join('');
|
|
195
|
+
if(accepted.has(hex)) continue;
|
|
196
|
+
let ok = false;
|
|
197
|
+
try { ok = schnorr.verify(signature, sighash, pubKey); } catch { ok = false; }
|
|
198
|
+
if(ok) accepted.set(hex, signature);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if(accepted.size < fallbackThreshold) {
|
|
202
|
+
throw new AggregationCohortError(
|
|
203
|
+
`Not enough valid fallback signatures: have ${accepted.size}, need ${fallbackThreshold}.`,
|
|
204
|
+
'NOT_ENOUGH_FALLBACK_SIGNATURES', { have: accepted.size, need: fallbackThreshold }
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A k-of-n CHECKSIGADD leaf demands EXACTLY k satisfied signatures (the final
|
|
209
|
+
// <k> NUMEQUAL fails if the running count differs), so inject exactly k.
|
|
210
|
+
const chosen = cohortXOnly
|
|
211
|
+
.filter(k => accepted.has(Array.from(k, b => b.toString(16).padStart(2, '0')).join('')))
|
|
212
|
+
.slice(0, fallbackThreshold);
|
|
213
|
+
|
|
214
|
+
tx.updateInput(inputIndex, {
|
|
215
|
+
tapLeafScript : [ leafEntry ],
|
|
216
|
+
tapScriptSig : chosen.map(pubKey => {
|
|
217
|
+
const hex = Array.from(pubKey, b => b.toString(16).padStart(2, '0')).join('');
|
|
218
|
+
return [ { pubKey, leafHash }, accepted.get(hex)! ];
|
|
219
|
+
}),
|
|
220
|
+
});
|
|
221
|
+
tx.finalize();
|
|
222
|
+
return tx;
|
|
223
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* {@link SILENT_LOGGER} to suppress output (useful for tests) or a custom
|
|
7
7
|
* implementation to route logs to pino, winston, Sentry, etc.
|
|
8
8
|
*
|
|
9
|
-
* The interface is intentionally small
|
|
9
|
+
* The interface is intentionally small: we don't want production code taking
|
|
10
10
|
* a hard dependency on any specific logger library.
|
|
11
11
|
*/
|
|
12
12
|
export interface Logger {
|
|
@@ -22,6 +22,12 @@ export type BaseBody = Partial<CohortConditions> & {
|
|
|
22
22
|
aggregatedNonce?: Uint8Array;
|
|
23
23
|
nonceContribution?: Uint8Array;
|
|
24
24
|
partialSignature?: Uint8Array;
|
|
25
|
+
/** Fallback (k-of-n script-path) leaf script, hex. Carried on FALLBACK_AUTHORIZATION_REQUEST. */
|
|
26
|
+
fallbackLeafScriptHex?: string;
|
|
27
|
+
/** Signer's x-only key for a fallback signature (which CHECKSIGADD slot it satisfies). */
|
|
28
|
+
signerPk?: Uint8Array;
|
|
29
|
+
/** A member's standalone BIP-340 fallback script-path signature (64 bytes). */
|
|
30
|
+
fallbackSignature?: Uint8Array;
|
|
25
31
|
pendingTx?: string;
|
|
26
32
|
/** Hex-encoded scriptPubKey of the UTXO being spent. Required for BIP-341 sighash. */
|
|
27
33
|
prevOutScriptHex?: string;
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
import type { SerializedSMTProof } from '@did-btcr2/smt';
|
|
15
15
|
import type { CohortConditions } from '../conditions.js';
|
|
16
|
+
import { KNOWN_FUNDING_MODELS } from '../conditions.js';
|
|
17
|
+
import { MAX_RECOVERY_SEQUENCE } from '../recovery-policy.js';
|
|
16
18
|
import type { BaseMessage } from './base.js';
|
|
17
19
|
import {
|
|
18
20
|
AGGREGATED_NONCE,
|
|
@@ -22,8 +24,11 @@ import {
|
|
|
22
24
|
COHORT_OPT_IN_ACCEPT,
|
|
23
25
|
COHORT_READY,
|
|
24
26
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
27
|
+
FALLBACK_AUTHORIZATION_REQUEST,
|
|
28
|
+
FALLBACK_SIGNATURE,
|
|
25
29
|
NONCE_CONTRIBUTION,
|
|
26
30
|
SIGNATURE_AUTHORIZATION,
|
|
31
|
+
SUBMIT_NONINCLUDED,
|
|
27
32
|
SUBMIT_UPDATE,
|
|
28
33
|
VALIDATION_ACK,
|
|
29
34
|
} from './constants.js';
|
|
@@ -59,6 +64,15 @@ export interface SubmitUpdateBody {
|
|
|
59
64
|
signedUpdate: Record<string, unknown>;
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
/**
|
|
68
|
+
* A member declines to submit an update this round (cooperative non-inclusion).
|
|
69
|
+
* Carries only the cohortId; membership is proven by the signed transport
|
|
70
|
+
* envelope (the sender DID), so no payload is needed.
|
|
71
|
+
*/
|
|
72
|
+
export interface SubmitNonIncludedBody {
|
|
73
|
+
cohortId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
export interface DistributeAggregatedDataBody {
|
|
63
77
|
cohortId: string;
|
|
64
78
|
beaconType: string;
|
|
@@ -100,6 +114,33 @@ export interface SignatureAuthorizationBody {
|
|
|
100
114
|
partialSignature: Uint8Array;
|
|
101
115
|
}
|
|
102
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Service asks members to authorize the k-of-n fallback (script-path) spend of
|
|
119
|
+
* the SAME beacon transaction (ADR 042). Carries the unsigned tx and the spent
|
|
120
|
+
* output so each member recomputes the script-path sighash itself. The fallback
|
|
121
|
+
* leaf script is included for cross-check; members recompute it from their own
|
|
122
|
+
* cohort state.
|
|
123
|
+
*/
|
|
124
|
+
export interface FallbackAuthorizationRequestBody {
|
|
125
|
+
cohortId: string;
|
|
126
|
+
sessionId: string;
|
|
127
|
+
pendingTx: string;
|
|
128
|
+
prevOutScriptHex: string;
|
|
129
|
+
prevOutValue: string;
|
|
130
|
+
fallbackLeafScriptHex: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A member's standalone BIP-340 signature over the fallback script-path sighash.
|
|
135
|
+
* `signerPk` is the member's x-only key (which CHECKSIGADD slot it satisfies).
|
|
136
|
+
*/
|
|
137
|
+
export interface FallbackSignatureBody {
|
|
138
|
+
cohortId: string;
|
|
139
|
+
sessionId: string;
|
|
140
|
+
signerPk: Uint8Array;
|
|
141
|
+
fallbackSignature: Uint8Array;
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
// ── Narrow message types (BaseMessage & { type, body }) ──────────────────
|
|
104
145
|
|
|
105
146
|
export type CohortAdvertMessage = BaseMessage & { type: typeof COHORT_ADVERT; body: CohortAdvertBody };
|
|
@@ -107,12 +148,15 @@ export type CohortOptInMessage = BaseMessage & { type: typeof COHORT_OPT_IN; bod
|
|
|
107
148
|
export type CohortOptInAcceptMessage = BaseMessage & { type: typeof COHORT_OPT_IN_ACCEPT; body: CohortOptInAcceptBody };
|
|
108
149
|
export type CohortReadyMessage = BaseMessage & { type: typeof COHORT_READY; body: CohortReadyBody };
|
|
109
150
|
export type SubmitUpdateMessage = BaseMessage & { type: typeof SUBMIT_UPDATE; body: SubmitUpdateBody };
|
|
151
|
+
export type SubmitNonIncludedMessage = BaseMessage & { type: typeof SUBMIT_NONINCLUDED; body: SubmitNonIncludedBody };
|
|
110
152
|
export type DistributeAggregatedDataMessage = BaseMessage & { type: typeof DISTRIBUTE_AGGREGATED_DATA; body: DistributeAggregatedDataBody };
|
|
111
153
|
export type ValidationAckMessage = BaseMessage & { type: typeof VALIDATION_ACK; body: ValidationAckBody };
|
|
112
154
|
export type AuthorizationRequestMessage = BaseMessage & { type: typeof AUTHORIZATION_REQUEST; body: AuthorizationRequestBody };
|
|
113
155
|
export type NonceContributionMessage = BaseMessage & { type: typeof NONCE_CONTRIBUTION; body: NonceContributionBody };
|
|
114
156
|
export type AggregatedNonceMessage = BaseMessage & { type: typeof AGGREGATED_NONCE; body: AggregatedNonceBody };
|
|
115
157
|
export type SignatureAuthorizationMessage = BaseMessage & { type: typeof SIGNATURE_AUTHORIZATION; body: SignatureAuthorizationBody };
|
|
158
|
+
export type FallbackAuthorizationRequestMessage = BaseMessage & { type: typeof FALLBACK_AUTHORIZATION_REQUEST; body: FallbackAuthorizationRequestBody };
|
|
159
|
+
export type FallbackSignatureMessage = BaseMessage & { type: typeof FALLBACK_SIGNATURE; body: FallbackSignatureBody };
|
|
116
160
|
|
|
117
161
|
/** Discriminated union of every well-formed aggregation message. */
|
|
118
162
|
export type AggregationMessage =
|
|
@@ -121,12 +165,15 @@ export type AggregationMessage =
|
|
|
121
165
|
| CohortOptInAcceptMessage
|
|
122
166
|
| CohortReadyMessage
|
|
123
167
|
| SubmitUpdateMessage
|
|
168
|
+
| SubmitNonIncludedMessage
|
|
124
169
|
| DistributeAggregatedDataMessage
|
|
125
170
|
| ValidationAckMessage
|
|
126
171
|
| AuthorizationRequestMessage
|
|
127
172
|
| NonceContributionMessage
|
|
128
173
|
| AggregatedNonceMessage
|
|
129
|
-
| SignatureAuthorizationMessage
|
|
174
|
+
| SignatureAuthorizationMessage
|
|
175
|
+
| FallbackAuthorizationRequestMessage
|
|
176
|
+
| FallbackSignatureMessage;
|
|
130
177
|
|
|
131
178
|
// ── Type guards ───────────────────────────────────────────────────────────
|
|
132
179
|
// Each guard validates `type` plus required body fields so it's safe to use
|
|
@@ -144,6 +191,16 @@ const optIntMin = (b: unknown, k: string, min: number): boolean => {
|
|
|
144
191
|
const v = b ? (b as Record<string, unknown>)[k] : undefined;
|
|
145
192
|
return v === undefined || (typeof v === 'number' && Number.isInteger(v) && v >= min);
|
|
146
193
|
};
|
|
194
|
+
/** Present, an integer, and within [min, max] inclusive. */
|
|
195
|
+
const intInRange = (b: unknown, k: string, min: number, max: number): boolean => {
|
|
196
|
+
const v = b ? (b as Record<string, unknown>)[k] : undefined;
|
|
197
|
+
return typeof v === 'number' && Number.isInteger(v) && v >= min && v <= max;
|
|
198
|
+
};
|
|
199
|
+
/** Absent, or present as a string in the allowed set. */
|
|
200
|
+
const optStrOneOf = (b: unknown, k: string, allowed: readonly string[]): boolean => {
|
|
201
|
+
const v = b ? (b as Record<string, unknown>)[k] : undefined;
|
|
202
|
+
return v === undefined || (typeof v === 'string' && allowed.includes(v));
|
|
203
|
+
};
|
|
147
204
|
const hasBool = (b: unknown, k: string): boolean =>
|
|
148
205
|
!!b && typeof (b as Record<string, unknown>)[k] === 'boolean';
|
|
149
206
|
const hasBytes = (b: unknown, k: string): boolean =>
|
|
@@ -164,6 +221,20 @@ export function isCohortAdvertMessage(m: BaseMessage): m is CohortAdvertMessage
|
|
|
164
221
|
&& optIntMin(m.body, 'maxParticipants', 1)
|
|
165
222
|
&& hasStr(m.body, 'beaconType')
|
|
166
223
|
&& hasStr(m.body, 'network')
|
|
224
|
+
// Recovery params are mandatory: a participant must see the recovery terms
|
|
225
|
+
// (key + timelock) before funding the beacon UTXO (ADR 042). recoverySequence
|
|
226
|
+
// is range-checked here because the participant funds based on the advert and
|
|
227
|
+
// does not separately run validateCohortConditions: an out-of-range value
|
|
228
|
+
// (e.g. one with the BIP-68 disable bit set) must be rejected before funding.
|
|
229
|
+
&& hasStr(m.body, 'recoveryKey')
|
|
230
|
+
&& intInRange(m.body, 'recoverySequence', 1, MAX_RECOVERY_SEQUENCE)
|
|
231
|
+
// fundingModel is optional on the wire (absent means the operator-funded
|
|
232
|
+
// default), but when present it must name a known model.
|
|
233
|
+
&& optStrOneOf(m.body, 'fundingModel', KNOWN_FUNDING_MODELS)
|
|
234
|
+
// fallbackThreshold is optional (absent means n-1 at keygen); when present it
|
|
235
|
+
// must be a positive integer. The upper bound against the cohort size is
|
|
236
|
+
// checked when the participant recomputes the beacon address.
|
|
237
|
+
&& optIntMin(m.body, 'fallbackThreshold', 1)
|
|
167
238
|
&& hasBytes(m.body, 'communicationPk');
|
|
168
239
|
}
|
|
169
240
|
|
|
@@ -191,6 +262,10 @@ export function isSubmitUpdateMessage(m: BaseMessage): m is SubmitUpdateMessage
|
|
|
191
262
|
&& !!m.body && typeof (m.body as Record<string, unknown>).signedUpdate === 'object';
|
|
192
263
|
}
|
|
193
264
|
|
|
265
|
+
export function isSubmitNonIncludedMessage(m: BaseMessage): m is SubmitNonIncludedMessage {
|
|
266
|
+
return m.type === SUBMIT_NONINCLUDED && hasStr(m.body, 'cohortId');
|
|
267
|
+
}
|
|
268
|
+
|
|
194
269
|
export function isDistributeAggregatedDataMessage(m: BaseMessage): m is DistributeAggregatedDataMessage {
|
|
195
270
|
return m.type === DISTRIBUTE_AGGREGATED_DATA
|
|
196
271
|
&& hasStr(m.body, 'cohortId')
|
|
@@ -233,3 +308,21 @@ export function isSignatureAuthorizationMessage(m: BaseMessage): m is SignatureA
|
|
|
233
308
|
&& hasStr(m.body, 'sessionId')
|
|
234
309
|
&& hasBytes(m.body, 'partialSignature');
|
|
235
310
|
}
|
|
311
|
+
|
|
312
|
+
export function isFallbackAuthorizationRequestMessage(m: BaseMessage): m is FallbackAuthorizationRequestMessage {
|
|
313
|
+
return m.type === FALLBACK_AUTHORIZATION_REQUEST
|
|
314
|
+
&& hasStr(m.body, 'cohortId')
|
|
315
|
+
&& hasStr(m.body, 'sessionId')
|
|
316
|
+
&& hasStr(m.body, 'pendingTx')
|
|
317
|
+
&& hasStr(m.body, 'prevOutScriptHex')
|
|
318
|
+
&& hasStr(m.body, 'prevOutValue')
|
|
319
|
+
&& hasStr(m.body, 'fallbackLeafScriptHex');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function isFallbackSignatureMessage(m: BaseMessage): m is FallbackSignatureMessage {
|
|
323
|
+
return m.type === FALLBACK_SIGNATURE
|
|
324
|
+
&& hasStr(m.body, 'cohortId')
|
|
325
|
+
&& hasStr(m.body, 'sessionId')
|
|
326
|
+
&& hasBytes(m.body, 'signerPk')
|
|
327
|
+
&& hasBytes(m.body, 'fallbackSignature');
|
|
328
|
+
}
|
|
@@ -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
|
|
|
@@ -18,6 +20,8 @@ export const COHORT_READY = `${AGGREGATION_MESSAGE_PREFIX}/keygen/cohort_ready`;
|
|
|
18
20
|
|
|
19
21
|
// Step 2 + 3: Update Submission, Aggregation, Validation
|
|
20
22
|
export const SUBMIT_UPDATE = `${AGGREGATION_MESSAGE_PREFIX}/update/submit_update`;
|
|
23
|
+
/** A member with no update this round declines explicitly (cooperative non-inclusion). */
|
|
24
|
+
export const SUBMIT_NONINCLUDED = `${AGGREGATION_MESSAGE_PREFIX}/update/submit_nonincluded`;
|
|
21
25
|
export const DISTRIBUTE_AGGREGATED_DATA = `${AGGREGATION_MESSAGE_PREFIX}/update/distribute_aggregated_data`;
|
|
22
26
|
export const VALIDATION_ACK = `${AGGREGATION_MESSAGE_PREFIX}/update/validation_ack`;
|
|
23
27
|
|
|
@@ -26,3 +30,11 @@ export const AUTHORIZATION_REQUEST = `${AGGREGATION_MESSAGE_PREFIX}/sign/authori
|
|
|
26
30
|
export const NONCE_CONTRIBUTION = `${AGGREGATION_MESSAGE_PREFIX}/sign/nonce_contribution`;
|
|
27
31
|
export const AGGREGATED_NONCE = `${AGGREGATION_MESSAGE_PREFIX}/sign/aggregated_nonce`;
|
|
28
32
|
export const SIGNATURE_AUTHORIZATION = `${AGGREGATION_MESSAGE_PREFIX}/sign/signature_authorization`;
|
|
33
|
+
|
|
34
|
+
// Step 4 fallback: k-of-n script-path signing when the optimistic n-of-n key
|
|
35
|
+
// path stalls (graceful liveness, ADR 042). A single standalone BIP-340
|
|
36
|
+
// signature per member, no nonce round.
|
|
37
|
+
/** Service asks members to sign the fallback (k-of-n) script path of the beacon tx. */
|
|
38
|
+
export const FALLBACK_AUTHORIZATION_REQUEST = `${AGGREGATION_MESSAGE_PREFIX}/sign/fallback_authorization_request`;
|
|
39
|
+
/** A member returns a standalone BIP-340 signature over the fallback script-path sighash. */
|
|
40
|
+
export const FALLBACK_SIGNATURE = `${AGGREGATION_MESSAGE_PREFIX}/sign/fallback_signature`;
|
|
@@ -8,8 +8,11 @@ import {
|
|
|
8
8
|
COHORT_OPT_IN_ACCEPT,
|
|
9
9
|
COHORT_READY,
|
|
10
10
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
11
|
+
FALLBACK_AUTHORIZATION_REQUEST,
|
|
12
|
+
FALLBACK_SIGNATURE,
|
|
11
13
|
NONCE_CONTRIBUTION,
|
|
12
14
|
SIGNATURE_AUTHORIZATION,
|
|
15
|
+
SUBMIT_NONINCLUDED,
|
|
13
16
|
SUBMIT_UPDATE,
|
|
14
17
|
VALIDATION_ACK,
|
|
15
18
|
} from './constants.js';
|
|
@@ -102,6 +105,22 @@ export function createSubmitUpdateMessage(fields: SubmitUpdateMessage): BaseMess
|
|
|
102
105
|
return new BaseMessage({ type: SUBMIT_UPDATE, from, to, body });
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
type SubmitNonIncludedMessage = {
|
|
109
|
+
from: string;
|
|
110
|
+
to: string;
|
|
111
|
+
cohortId: string;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Factory for a Submit Non-Included message: a member declares it has no update
|
|
116
|
+
* this round (cooperative non-inclusion). Membership is proven by the signed
|
|
117
|
+
* envelope, so the body carries only the cohortId.
|
|
118
|
+
*/
|
|
119
|
+
export function createSubmitNonIncludedMessage(fields: SubmitNonIncludedMessage): BaseMessage {
|
|
120
|
+
const { from, to, ...body } = fields;
|
|
121
|
+
return new BaseMessage({ type: SUBMIT_NONINCLUDED, from, to, body });
|
|
122
|
+
}
|
|
123
|
+
|
|
105
124
|
/**
|
|
106
125
|
* Step 3: Aggregate & Validate
|
|
107
126
|
* Factory functions for creating messages related to the aggregate and validate step, where
|
|
@@ -238,3 +257,41 @@ export function createSignatureAuthorizationMessage(fields: SignatureAuthorizati
|
|
|
238
257
|
const { from, to, ...body } = fields;
|
|
239
258
|
return new BaseMessage({ type: SIGNATURE_AUTHORIZATION, from, to, body });
|
|
240
259
|
}
|
|
260
|
+
|
|
261
|
+
type FallbackAuthorizationRequestMessage = {
|
|
262
|
+
from: string;
|
|
263
|
+
to: string;
|
|
264
|
+
cohortId: string;
|
|
265
|
+
sessionId: string;
|
|
266
|
+
pendingTx: string;
|
|
267
|
+
prevOutScriptHex: string;
|
|
268
|
+
prevOutValue: string;
|
|
269
|
+
fallbackLeafScriptHex: string;
|
|
270
|
+
};
|
|
271
|
+
type FallbackSignatureMessage = {
|
|
272
|
+
from: string;
|
|
273
|
+
to: string;
|
|
274
|
+
cohortId: string;
|
|
275
|
+
sessionId: string;
|
|
276
|
+
signerPk: Uint8Array;
|
|
277
|
+
fallbackSignature: Uint8Array;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Factory for a Fallback Authorization Request: the service asks members to
|
|
282
|
+
* authorize the k-of-n script-path spend of the beacon transaction when the
|
|
283
|
+
* optimistic n-of-n key path stalls (ADR 042).
|
|
284
|
+
*/
|
|
285
|
+
export function createFallbackAuthorizationRequestMessage(fields: FallbackAuthorizationRequestMessage): BaseMessage {
|
|
286
|
+
const { from, to, ...body } = fields;
|
|
287
|
+
return new BaseMessage({ type: FALLBACK_AUTHORIZATION_REQUEST, from, to, body });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Factory for a Fallback Signature: a member's standalone BIP-340 signature over
|
|
292
|
+
* the fallback script-path sighash (no nonce round).
|
|
293
|
+
*/
|
|
294
|
+
export function createFallbackSignatureMessage(fields: FallbackSignatureMessage): BaseMessage {
|
|
295
|
+
const { from, to, ...body } = fields;
|
|
296
|
+
return new BaseMessage({ type: FALLBACK_SIGNATURE, from, to, body });
|
|
297
|
+
}
|
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
COHORT_OPT_IN_ACCEPT,
|
|
7
7
|
COHORT_READY,
|
|
8
8
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
9
|
+
FALLBACK_AUTHORIZATION_REQUEST,
|
|
10
|
+
FALLBACK_SIGNATURE,
|
|
9
11
|
NONCE_CONTRIBUTION,
|
|
10
12
|
SIGNATURE_AUTHORIZATION,
|
|
13
|
+
SUBMIT_NONINCLUDED,
|
|
11
14
|
SUBMIT_UPDATE,
|
|
12
15
|
VALIDATION_ACK,
|
|
13
16
|
} from './constants.js';
|
|
@@ -21,6 +24,7 @@ const KEYGEN_VALUES: Set<string> = new Set([
|
|
|
21
24
|
|
|
22
25
|
const UPDATE_VALUES: Set<string> = new Set([
|
|
23
26
|
SUBMIT_UPDATE,
|
|
27
|
+
SUBMIT_NONINCLUDED,
|
|
24
28
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
25
29
|
VALIDATION_ACK,
|
|
26
30
|
]);
|
|
@@ -30,6 +34,8 @@ const SIGN_VALUES: Set<string> = new Set([
|
|
|
30
34
|
NONCE_CONTRIBUTION,
|
|
31
35
|
AGGREGATED_NONCE,
|
|
32
36
|
SIGNATURE_AUTHORIZATION,
|
|
37
|
+
FALLBACK_AUTHORIZATION_REQUEST,
|
|
38
|
+
FALLBACK_SIGNATURE,
|
|
33
39
|
]);
|
|
34
40
|
|
|
35
41
|
/**
|